//////////////////////////////
// ProSonic Engine			//
// Written by Damian Grove	//
//////////////////////////////

// TO-DO LIST:
//
// - Create working MMF-like scripting language
// - Create working background layer
// - Create configuration file
// - Allow sprite/animation and sound files to be loaded by external list
// - Allow block art to be animated
// - Get water working
// - Add object layout editor
// - *** Make the engine faster ***

// BUGS:
//
// - Fix block editor
// - Fix collision editor

// NOTE:
//
// Look into optimizing draw.c by keeping track of X and Y screen drawing position

// TEST:
//
// CPU		700 MHz Celeron
// RAM		128 MB
// OS		TinyXP rev 09
// DATE		07/02/2009
// FPS RATES (with vsync off)
//		256 x 192: 45
//		320 x 200: 38
//		320 x 224: 35
//		400 x 300: 23
//		512 x 384: 15
//		640 x 480: 10
//		800 x 600: 07

// GOAL: Get 932x700 running 60 fps on P4 machine
// Frame rate as of 08/05/2009 -- 37 fps

#include	<stdio.h>
#include	<stdlib.h>		// used for exit()
#include	<string.h>
#include	<math.h>
#include	<allegro.h>

#ifndef ALLEGRO_DOS
	#include	<nl.h>
#endif

//#include	<winalleg.h>
//#include	<windows.h>

#include	"zlib.h"
#include	"draw.h"
#include	"tools.h"
#include	"mouse.h"
#include	"strings.h"
#include	"error.h"
#include	"menus.h"
#include	"objects.h"
#include	"sprites.h"
#include	"m68k.h"
#include	"obj01.h"
#include	"script.h"
#include	"network.h"

#include	"ym2612.h"
#include	"psg.h"



void TicUpdate(void)	// runs every 1/60 seconds
{
	ticUpdated = 1;

	if(!Game_paused){
		ticCounterR++;
		ticCounter60R++;

		if(ticCounter60R >= 60){
			ticCounter60R = 0;
			fps = ticCounter60L;
			ticCounter60L = 0;
		}
	}

	#ifdef ENABLE_MOUSE
		if(waitCounter > 0)
			waitCounter--;
	#endif
}
END_OF_FUNCTION(TicUpdate);



#ifndef ALLEGRO_DOS
void close_button_handler(void){
	close_button_pressed = TRUE;
}
END_OF_FUNCTION(close_button_handler);
#endif



void quit(int e){
	#ifdef ALLEGRO_DOS
	set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
	system("CLS");
	#endif

	if(e != 0){
		switch(e){
			case ERROR_OBJECT_BUFFER:
				allegro_message("%s", STR_ERR_OBJECT_BUFFER);
				break;
			case ERROR_DIVIDE_ZERO:
				allegro_message("%s", STR_ERR_DIVIDE_ZERO);
				break;
			case ERROR_INIT_SCREEN:
				allegro_message("%s", STR_ERR_INIT_SCREEN);
				break;
			case ERROR_NO_DEMO_FILES:
				allegro_message("%s", STR_ERR_NO_DEMO_FILES);
				break;
			default:
				allegro_message("%s", STR_ERR_UNKNOWN);
		}
	}

	if(program_filename)	free(program_filename);

	#ifdef AUDIO_REC_ENABLED
		i = ftell(pcm_out) - 8;
		fseek(pcm_out, 0x4, SEEK_SET);
		fwrite(&i, 4, 1, pcm_out);
		i -= 0x24;
		fseek(pcm_out, 0x28, SEEK_SET);
		fwrite(&i, 4, 1, pcm_out);
		fclose(pcm_out);
	#endif
	remove_timer();	// needed to prevent crashes
	stop_audio_stream(stream);
	YM2612_End();
	if(fm_data)		free(fm_data);
	if(pcm_data)	free(pcm_data);

	#ifdef ENABLE_SRAM_LOG_ANALYSIS
	if(SRAMFile != NULL)
		fclose(SRAMFile);
	#endif
	if(DemoFile != NULL)
		fclose(DemoFile);

	for(i=0; i<4; i++){
		if(LayerB[i]) destroy_bitmap(LayerB[i]);
		if(LayerL[i]) destroy_bitmap(LayerL[i]);
		if(LayerH[i]) destroy_bitmap(LayerH[i]);
		if(LayerSH[i]) destroy_bitmap(LayerSH[i]);
	}
	if(LayerM) destroy_bitmap(LayerM);

	DeleteZone(&z);
	if(COMPILED_MAP)		free(COMPILED_MAP);
	if(COMPILED_BACKMAP)	free(COMPILED_BACKMAP);
	if(titlecard_data)		free(titlecard_data);
	if(demodata)			free(demodata);
	if(script)				free(script);
	if(obj_script)			free(obj_script);
	if(Objects)				free(Objects);
	ClearSprites();

	if(message)				free(message);
	if(message_time)		free(message_time);
	#ifndef ALLEGRO_DOS
	if(prosonicnet)			nlGroupDestroy(prosonicnet);
	if(sock_server != NL_INVALID)	nlClose(sock_server);
	if(sock_client != NL_INVALID)	nlClose(sock_client);
	if(game_socket[0] != NL_INVALID)		nlClose(game_socket[0]);
	if(game_socket[1] != NL_INVALID)		nlClose(game_socket[1]);
	if(game_socket[2] != NL_INVALID)		nlClose(game_socket[2]);
	if(game_socket[3] != NL_INVALID)		nlClose(game_socket[3]);
	nlShutdown();
	#endif

	if(e == 0){
		set_config_int(config_section[num_of_frames-1], "digi_card", digi_card);
		set_config_int(config_section[num_of_frames-1], "audio_volume", audio_volume);
		set_config_int(config_section[num_of_frames-1], "smart_mix", smart_mix);
		set_config_int(config_section[num_of_frames-1], "vsync_enable", vsync_enable);
		set_config_int(config_section[num_of_frames-1], "frame_skip", frame_skip);
		set_config_int(config_section[num_of_frames-1], "screen_resolution_x", screen_resolution_x);
		set_config_int(config_section[num_of_frames-1], "screen_resolution_y", screen_resolution_y);
		set_config_int(config_section[num_of_frames-1], "screen_padding_x", screen_padding_x);
		set_config_int(config_section[num_of_frames-1], "screen_padding_y", screen_padding_y);
		set_config_int(config_section[num_of_frames-1], "screen_double_x", screen_double_x);
		set_config_int(config_section[num_of_frames-1], "screen_double_y", screen_double_y);
		set_config_int(config_section[num_of_frames-1], "full_screen", full_screen);
		for(i=0; i < 4; i++){
			set_config_int(config_section[i], "bind_up", key_bind[i].bind_up);
			set_config_int(config_section[i], "bind_down", key_bind[i].bind_down);
			set_config_int(config_section[i], "bind_left", key_bind[i].bind_left);
			set_config_int(config_section[i], "bind_right", key_bind[i].bind_right);
			set_config_int(config_section[i], "bind_b", key_bind[i].bind_b);
			set_config_int(config_section[i], "bind_c", key_bind[i].bind_c);
			set_config_int(config_section[i], "bind_a", key_bind[i].bind_a);
			set_config_int(config_section[i], "bind_start", key_bind[i].bind_start);
		}
	}

	allegro_exit();

	exit(1);
}



////////// START ---- * NETWORK STUFF * //////////
// Move all network code into a seperate file in the future //
#ifndef ALLEGRO_DOS
void GetNetworkData(){
	unsigned char type;
	unsigned char sender;
	unsigned char ready;

	if(net_type == 2){			// client
		//allegro_message("GetNetworkData() -- client");
		type = 0;
		while(type != 0xFF){
			i = nlRead(sock_client, &type, 1);
			if(i > 0){
				switch(type){
					case 0x01:
						n = 0;
						while(n != 1){
							n = nlRead(sock_client, &sender, 1);
							if(key[KEY_ESC])
								quit(0);
						}
						n = 0;
						while(n != 63){
							n = nlRead(sock_client, talk_incoming, 63);
							if(key[KEY_ESC])
								quit(0);
						}
						allegro_message("CLIENT INCOMING: %s", talk_incoming);
						break;
					case 0xFF:
						nlRead(sock_client, &sender, 1);
						break;
				}
				//allegro_message("GetNetworkData() -- client\ntype = %i\nsender = %i", type, sender);
			}
			if(key[KEY_7])
				allegro_message("GetNetworkData() -- client");
			if(key[KEY_ESC])
				quit(0);
		}
	}
	else if(net_type == 1){		// server
		//allegro_message("GetNetworkData() -- server");
		i = 1;
		type = 0;
		while(i > 0){
			i = nlRead(game_socket[0], &type, 1);
			if(i > 0){
				switch(type){
					case 1:
						n = 0;
						while(n != 1){
							n = nlRead(game_socket[0], &sender, 1);
							if(key[KEY_ESC])
								quit(0);
						}
						n = 0;
						while(n != 63){
							n = nlRead(game_socket[0], talk_incoming, 63);
							if(key[KEY_ESC])
								quit(0);
						}
						allegro_message("SERVER INCOMING: %s", talk_incoming);
						break;
				}
				//allegro_message("GetNetworkData() -- server\ntype = %i\nsender = %i", type, sender);
			}
			if(key[KEY_7])
				allegro_message("GetNetworkData() -- server");
			if(key[KEY_ESC])
				quit(0);
		}
	}
}

void ManageNetwork(){
	int sock;
	unsigned char type;
	unsigned char sender;
	unsigned char ready = 1;

	//allegro_message("ManageNetwork()");
	while(ready != 0x3){
		for(sock=0; sock < 2; sock++){
			i = 1;
			type = 0;
			while(i > 0){
				i = nlRead(game_socket[sock], &type, 1);
				if(i > 0){
					switch(type){
						case 0x01:
							n = 0;
							while(n != 1){
								n = nlRead(game_socket[sock], &sender, 1);
								if(key[KEY_ESC])
									quit(0);
							}
							n = 0;
							while(n != 63){
								n = nlRead(game_socket[sock], talk_incoming, 63);
								if(key[KEY_ESC])
									quit(0);
							}
							allegro_message("type = 0x%02X\nsender = 0x%02X\ntalk_incoming = %s", type, sender, talk_incoming);
							if(sender == 0){
								// Relay message from player 1 to player 2
								n = 0;
								while(n != 1){
									n = nlWrite(game_socket[1], &type, 1);
									if(key[KEY_ESC])
										quit(0);
								}
								n = 0;
								while(n != 1){
									n = nlWrite(game_socket[1], &sender, 1);
									if(key[KEY_ESC])
										quit(0);
								}
								n = 0;
								while(n != 63){
									n = nlWrite(game_socket[1], talk_incoming, 63);
									if(key[KEY_ESC])
										quit(0);
								}
							}
							else if(sender == 1){
								// Relay message from player 2 to player 1
								n = 0;
								while(n != 1){
									n = nlWrite(game_socket[0], &type, 1);
									if(key[KEY_ESC])
										quit(0);
								}
								n = 0;
								while(n != 1){
									n = nlWrite(game_socket[0], &sender, 1);
									if(key[KEY_ESC])
										quit(0);
								}
								n = 0;
								while(n != 63){
									n = nlWrite(game_socket[0], talk_incoming, 63);
									if(key[KEY_ESC])
										quit(0);
								}
							}
							break;
						case 0xFF:
							n = 0;
							while(n != 1){
								n = nlRead(game_socket[sock], &sender, 1);
								if(key[KEY_ESC])
									quit(0);
							}
							ready |= (1 << sender);
							break;
					}
					//allegro_message("ManageNetwork()\ntype = %i\nsender = %i", type, sender);
				}
				if(key[KEY_7])
					allegro_message("ManageNetwork()");
				if(key[KEY_ESC])
					quit(0);
			}
		}
	}
	i = 0xFF;
	n = 0;
	while(n != 1){
		n = nlWrite(prosonicnet, &i, 1);
		if(key[KEY_ESC])
			quit(0);
	}
	n = 0;
	while(n != 1){
		n = nlWrite(prosonicnet, &net_id[0], 1);
		if(key[KEY_ESC])
			quit(0);
	}
}

int check_commands(char *string){
	if(string[0] != '/')
		return 0;

	for(i=1; i<64; i++){
		if(string[i] >= 'a' && string[i] <= 'z')
			string[i] -= 0x20;	// convert to uppercase
	}

	if(strcmp("/QUIT\n", string) == 0)
		return 1;

	return 0;
}

int open_socket(NLsocket *s, short local_port, short net_port, const char *net_ip, ...){
	*s = nlOpen(local_port, NL_RELIABLE);

	if(*s == NL_INVALID){
		allegro_message("ERROR: Invalid socket!\n");
		nlClose(*s);
		return 1;
	}

	nlGetAddrFromName(net_ip, &addr_client);
	nlSetAddrPort(&addr_client, net_port);

	if(nlConnect(*s, &addr_client) == NL_FALSE){
		i = nlGetError();
		switch(i){
			case NL_INVALID_SOCKET:
				allegro_message("NL_INVALID_SOCKET\n");
				nlClose(*s);
				return;
			case NL_NULL_POINTER:
				allegro_message("NL_NULL_POINTER\n");
				nlClose(*s);
				return;
			case NL_SYSTEM_ERROR:
				allegro_message("NL_SYSTEM_ERROR: %s\n", nlGetSystemErrorStr(nlGetSystemError()));
				nlClose(*s);
				return;
			case NL_CON_REFUSED:
				allegro_message("NL_CON_REFUSED\n");
				nlClose(*s);
				return;
			case NL_WRONG_TYPE:
				allegro_message("NL_WRONG_TYPE\n");
				nlClose(*s);
				return;
			default:
				allegro_message("ERROR i = 0x%X\n", i);
				//system("PAUSE");
		}
		return 1;
	}

	return 0;
}

void serverside_setup(){
	sock_server = nlOpen(port, NL_RELIABLE);

	if(sock_server == NL_INVALID){
		allegro_message("ERROR: Invalid socket!\n");
		nlClose(sock_server);
		return;
	}

	if(nlListen(sock_server) == NL_FALSE){
		allegro_message("ERROR: Unable to listen!\n");
		nlClose(sock_server);
		return;
	}

	prosonicnet = nlGroupCreate();
	if(prosonicnet == NL_INVALID){
		allegro_message("ERROR: Unable to create group!\n");
		nlClose(sock_server);
		return;
	}

	//printf("Listening...\n");
	i=0;
	while(i==0){
		game_socket[1] = nlAcceptConnection(sock_server);
		if(game_socket[1] == NL_INVALID){
			i = nlGetError();
			switch(i){
				case NL_NOT_LISTEN:
					allegro_message("NL_NOT_LISTEN\n"); nlClose(sock_server); return;
				case NL_NO_PENDING:
				/*	allegro_message("NL_NO_PENDING\n");*/ break;
				case NL_WRONG_TYPE:
					allegro_message("NL_WRONG_TYPE\n"); nlClose(sock_server); return;
				default:
					allegro_message("ERROR i = 0x%X\n", i); //system("PAUSE");
			}
			i = 0;	// continue loop
		}
		else{
			// Verify network connection and ProSonic version
			strcpy(talk_outgoing, "PROSONIC\n");
			strcat(talk_outgoing, __DATE__);
			strcat(talk_outgoing, "\n");
			strcat(talk_outgoing, __TIME__);
			nlWrite(game_socket[1], talk_outgoing, 30);
			i = 0;
			while(i == 0){
				i = nlRead(game_socket[1], talk_incoming, 30);
				if(i == 30){
					if(strcmp(talk_incoming, talk_outgoing) != 0){
						allegro_message("ERROR: Client has incompatible version");
						nlClose(game_socket[1]); return;
					}
				}
				else if(i != 0){
					allegro_message("ERROR: Unexpected data recieved");
					nlClose(game_socket[1]); return;
				}
			}
			for(i=0; i<64; i++)
				talk_incoming[i] = 0;
			for(i=0; i<64; i++)
				talk_outgoing[i] = 0;

			if(open_socket(&game_socket[0], 0, 727, "127.0.0.1") == 0){
				//nlGroupAddSocket(prosonicnet, game_socket[0]);
				nlGroupAddSocket(prosonicnet, game_socket[1]);
				net_id[0] = 0;
				allegro_message("Connection successful!");
			}
			else{
				nlClose(game_socket[1]); return;
			}
		}
	}
}

void clientside_setup(){
	sock_client = nlOpen(0, NL_RELIABLE);

	if(sock_client == NL_INVALID){
		allegro_message("ERROR: Invalid socket!\n");
		nlClose(sock_client);
		return;
	}

	prosonicnet = nlGroupCreate();
	if(prosonicnet == NL_INVALID){
		allegro_message("ERROR: Unable to create group!\n");
		nlClose(sock_client);
		return;
	}

	nlGetAddrFromName(ip_input, &addr_client);
	nlSetAddrPort(&addr_client, port);

	//printf("Connecting...\n");
	i=0;
	while(i==0){
		if(nlConnect(sock_client, &addr_client) == NL_FALSE){
			i = nlGetError();
			switch(i){
				case NL_INVALID_SOCKET:
					allegro_message("NL_INVALID_SOCKET\n"); nlClose(sock_client); return;
				case NL_NULL_POINTER:
					allegro_message("NL_NULL_POINTER\n"); nlClose(sock_client); return;
				case NL_SYSTEM_ERROR:
					allegro_message("NL_SYSTEM_ERROR: %s\n", nlGetSystemErrorStr(nlGetSystemError())); nlClose(sock_client); return;
				case NL_CON_REFUSED:
					allegro_message("NL_CON_REFUSED\n"); nlClose(sock_client); return;
				case NL_WRONG_TYPE:
					allegro_message("NL_WRONG_TYPE\n"); nlClose(sock_client); return;
				default:
					allegro_message("ERROR i = 0x%X\n", i); //system("PAUSE");
			}
			i = 0;	// continue loop
		}
		else{
			// Verify network connection and ProSonic version
			strcpy(talk_outgoing, "PROSONIC\n");
			strcat(talk_outgoing, __DATE__);
			strcat(talk_outgoing, "\n");
			strcat(talk_outgoing, __TIME__);
			while(i==0){
				i = nlRead(sock_client, talk_incoming, 30);
				if(i == 30){
					if(strcmp(talk_incoming, talk_outgoing) != 0){
						nlWrite(sock_client, talk_outgoing, 30);
						allegro_message("ERROR: Incompatible version");
						nlClose(sock_client); return;
					}
				}
				else if(i != 0){
					nlWrite(sock_client, talk_outgoing, 31);
					allegro_message("ERROR: Unexpected data recieved");
					nlClose(sock_client); return;
				}
			}
			nlWrite(sock_client, talk_outgoing, 30);
			for(i=0; i<64; i++)
				talk_incoming[i] = 0;
			for(i=0; i<64; i++)
				talk_outgoing[i] = 0;
			nlGroupAddSocket(prosonicnet, sock_client);
			net_id[0] = 1;
			allegro_message("Connection successful!");
		}
	}
}
#endif
////////// END ---- * NETWORK STUFF * //////////



////////// START ---- * SOUND STUFF * //////////
// Move all sound code into a seperate file in the future //
void ResetSound(){
	fm_data_pos[0] = 0;
	fm_data_pos[1] = 0;
	fm_data_pos[2] = 0;
	fm_data_pos[3] = 0;
	pcm_data_pos[0][0] = 0;
	pcm_data_pos[1][0] = 0;
	pcm_data_pos[0][1] = 0;
	pcm_data_pos[1][1] = 0;
	pcm_data_pos[0][2] = 0;
	pcm_data_pos[1][2] = 0;
	pcm_data_pos[0][3] = 0;
	pcm_data_pos[1][3] = 0;
}

char PauseSound(){
	short *temp;

	// This code will clear the buffer if the game is paused
	if(Game_paused && advance_frame == 0){
		temp = (short*) get_audio_stream_buffer(stream);
		if(temp){
			for( i = 0 ; i < 1470 ; i++ )
				temp[i] = 0x8000;
			free_audio_stream_buffer(stream);
		}
		return 1;
	}

	return 0;
}



FUNCINLINE void ProcessSound(){
	short *temp;
	short byte1=0;
	short byte2=0;
	short byte3=0;
	short byte4=0;
	double v;

	int audio_buffer_1[2][735];
	int audio_buffer_2[2][735];
	int audio_buffer_3[2][735];
	int audio_buffer_4[2][735];

	//int audio_buffer_1_used = (pcm_data_poll[0][0] || pcm_data_poll[1][0]);
	//int audio_buffer_2_used = (pcm_data_poll[0][1] || pcm_data_poll[1][1]);
	//int audio_buffer_3_used = (pcm_data_poll[0][2] || pcm_data_poll[1][2]);
	//int audio_buffer_4_used = (pcm_data_poll[0][3] || pcm_data_poll[1][3]);

	pcm_data_poll[0][0] = 0;
	pcm_data_poll[1][0] = 0;



	if(PauseSound())
		return;



	// Voice 1 (BGM)
	RestoreYM2612(0);
	RestorePSG(0);
	if(!fm_data_pos[0])
		StreamPCM(0, &pcm_data_pos[0][0], &pcm_data_pos[1][0], &pcm_data_poll[0][0], &pcm_data_poll[1][0], audio_buffer_1[0], audio_buffer_1[1]);
	else{
		StreamFM(0, &fm_data_pos[0], audio_buffer_1[0], audio_buffer_1[1]);
		BackupYM2612(0);
		BackupPSG(0);
	}

	// Voice 2
	RestoreYM2612(1);
	RestorePSG(1);
	if(!fm_data_pos[1])
		StreamPCM(1, &pcm_data_pos[0][1], &pcm_data_pos[1][1], &pcm_data_poll[0][1], &pcm_data_poll[1][1], audio_buffer_2[0], audio_buffer_2[1]);
	else{
		StreamFM(1, &fm_data_pos[1], audio_buffer_2[0], audio_buffer_2[1]);
		BackupYM2612(1);
		BackupPSG(1);
	}

	// Voice 3
	RestoreYM2612(2);
	RestorePSG(2);
	if(!fm_data_pos[2])
		StreamPCM(2, &pcm_data_pos[0][2], &pcm_data_pos[1][2], &pcm_data_poll[0][2], &pcm_data_poll[1][2], audio_buffer_3[0], audio_buffer_3[1]);
	else{
		StreamFM(2, &fm_data_pos[2], audio_buffer_3[0], audio_buffer_3[1]);
		BackupYM2612(2);
		BackupPSG(2);
	}

	// Voice 4
	RestoreYM2612(3);
	RestorePSG(3);
	if(!fm_data_pos[3])
		StreamPCM(3, &pcm_data_pos[0][3], &pcm_data_pos[1][3], &pcm_data_poll[0][3], &pcm_data_poll[1][3], audio_buffer_4[0], audio_buffer_4[1]);
	else{
		StreamFM(3, &fm_data_pos[3], audio_buffer_4[0], audio_buffer_4[1]);
		BackupYM2612(3);
		BackupPSG(3);
	}

	temp = (short*) get_audio_stream_buffer(stream);
	if(temp)  {
		for( i = 0 ; i < 1470 ; i++ ){
			byte1 = (short)(audio_buffer_1[i&1][i>>1]);
			byte2 = (short)(audio_buffer_2[i&1][i>>1]);
			byte3 = (short)(audio_buffer_3[i&1][i>>1]);
			byte4 = (short)(audio_buffer_4[i&1][i>>1]);

			byte1 *= ((float)(audio_volume+1) / 32);
			byte2 *= ((float)(audio_volume+1) / 32);
			byte3 *= ((float)(audio_volume+1) / 32);
			byte4 *= ((float)(audio_volume+1) / 32);

			if(smart_mix){
				n = byte1 + byte2 + byte3 + byte4;
				//if(abs(n) > audio_peak)
				//	audio_peak = n;

				// Compression level 0
				if(n < 32768 && n > -32769){
					mix_volume = 0;
					audio_compression = 0x10;
				}
				// Compression level 1
				else if(n < 65536 && n > -65537){
					mix_volume = -6.020600;
					audio_compression = 0x25;
				}
				// Compression level 2
				else{
					mix_volume = -9.542425;
					audio_compression = 0x3E;
				}

				// COMPRESSOR
				if(audio_compression != 0x10){
					byte1 ^= 0x8000;
					v = byte1;
					if(v < 0)
						v = pow(10, (log10(-v / 32767) * (float)((float)audio_compression / 16))) * -32767;
					else if(v > 0)
						v = pow(10, (log10(v / 32767) * (float)((float)audio_compression / 16))) * 32767;
					byte1 = v;
					byte1 ^= 0x8000;

					byte2 ^= 0x8000;
					v = byte2;
					if(v < 0)
						v = pow(10, (log10(-v / 32767) * (float)((float)audio_compression / 16))) * -32767;
					else if(v > 0)
						v = pow(10, (log10(v / 32767) * (float)((float)audio_compression / 16))) * 32767;
					byte2 = v;
					byte2 ^= 0x8000;

					byte3 ^= 0x8000;
					v = byte3;
					if(v < 0)
						v = pow(10, (log10(-v / 32767) * (float)((float)audio_compression / 16))) * -32767;
					else if(v > 0)
						v = pow(10, (log10(v / 32767) * (float)((float)audio_compression / 16))) * 32767;
					byte3 = v;
					byte3 ^= 0x8000;

					byte4 ^= 0x8000;
					v = byte4;
					if(v < 0)
						v = pow(10, (log10(-v / 32767) * (float)((float)audio_compression / 16))) * -32767;
					else if(v > 0)
						v = pow(10, (log10(v / 32767) * (float)((float)audio_compression / 16))) * 32767;
					byte4 = v;
					byte4 ^= 0x8000;
				}

				// MIXER
				if(mix_volume != 0){
					v = byte1;
					if(v < 0)
						v = pow(10, log10(-v / 32767) + (mix_volume / 20)) * -32767;
					else if(v > 0)
						v = pow(10, log10(v / 32767) + (mix_volume / 20)) * 32767;
					byte1 = v;

					v = byte2;
					if(v < 0)
						v = pow(10, log10(-v / 32767) + (mix_volume / 20)) * -32767;
					else if(v > 0)
						v = pow(10, log10(v / 32767) + (mix_volume / 20)) * 32767;
					byte2 = v;

					v = byte3;
					if(v < 0)
						v = pow(10, log10(-v / 32767) + (mix_volume / 20)) * -32767;
					else if(v > 0)
						v = pow(10, log10(v / 32767) + (mix_volume / 20)) * 32767;
					byte3 = v;

					v = byte4;
					if(v < 0)
						v = pow(10, log10(-v / 32767) + (mix_volume / 20)) * -32767;
					else if(v > 0)
						v = pow(10, log10(v / 32767) + (mix_volume / 20)) * 32767;
					byte4 = v;
				}
			}

			// Write the mix to the audio stream
			temp[i] = (int)(byte1 + byte2 + byte3 + byte4)^0x8000;
		}

		#ifdef AUDIO_REC_ENABLED
			RecordPCM(temp);
		#endif

		free_audio_stream_buffer(stream);
	}

	if(fm_fade_out[0])	fm_fader[0]++;
	if(fm_fade_out[1])	fm_fader[1]++;
	if(fm_fade_out[2])	fm_fader[2]++;
	if(fm_fade_out[3])	fm_fader[3]++;

	if(fm_fader[0] == 160){ PlayFM(0, 0, 0); }
	if(fm_fader[1] == 160){ PlayFM(0, 1, 0); }
	if(fm_fader[2] == 160){ PlayFM(0, 2, 0); }
	if(fm_fader[3] == 160){ PlayFM(0, 3, 0); }
}



FUNCINLINE void FadeOutFM(char buffer){
	fm_fade_out[buffer] = 1;
}



void LoadFM(unsigned char file, const char *string, char type, char dac, char repeat){
	char vgm_header[4];
	gzFile *gzf;
	int gzc_buffer_size;
	int gzu_buffer_size;
	char *gzc_buffer;
	char *gzu_buffer;

	#ifdef ALLEGRO_DOS
	allegro_message("Loading FM file \"%s\"...\n", string);
	#endif

	if(string[0] == '\0'){
		fm_data_type[file] = -1;
		fm_data_dac[file] = 0;
		fm_data_repeat[file] = 0;
		fm_data_address[file] = 0;
		fm_size += 4;
		if(fm_data == 0)
			fm_data = (char *)malloc(fm_size);
		else
			fm_data = (char *)realloc(fm_data, fm_size);
	}
	else{
		ym_log_file = fopen(string, "rb");
		if(ym_log_file == NULL){
			allegro_message("Could not open file \"%s\"...\n", string);
			return;
		}
		fm_data_type[file] = type;
		fm_data_dac[file] = dac;
		fm_data_repeat[file] = repeat;
		fm_data_address[file] = fm_size;

		if(type == FM_TYPE_VGM){
			// Read the header to see if it's compressed or not
			vgm_header[0] = getc(ym_log_file);
			vgm_header[1] = getc(ym_log_file);
			vgm_header[2] = getc(ym_log_file);
			vgm_header[3] = '\0';
		}

		if(type == FM_TYPE_VGM && strcmp(vgm_header, "Vgm") != 0){
			// The VGM file is compressed, so we must decompress it
			fseek(ym_log_file, 0, SEEK_END);
			gzc_buffer_size = ftell(ym_log_file);	// record size of GZ file
			rewind(ym_log_file);
			gzc_buffer = (char *)malloc(gzc_buffer_size);
			fread(gzc_buffer, 1, gzc_buffer_size, ym_log_file);
			fclose(ym_log_file);

			n = gzc_buffer_size * 10;	// guess at size of VGM file
			gzu_buffer_size = 0;
			gzu_buffer = (char *)malloc(n);
			gzf = gzopen(string, "rb");
			i = gzread(gzf, gzu_buffer, n);
			gzu_buffer_size += i;
			while(i == n){
				// As long as there's more to read, we keep resizing and reading
				gzu_buffer = (char *)realloc(gzu_buffer, gzu_buffer_size + i);
				i = gzread(gzf, &gzu_buffer[gzu_buffer_size], n);
				gzu_buffer_size += i;
			}
			gzclose(gzf);

			free(gzc_buffer);

			fm_size += (gzu_buffer_size+4);
			if(fm_data == 0)
				fm_data = (char *)malloc(fm_size);
			else
				fm_data = (char *)realloc(fm_data, fm_size);
			for(i=0; i < gzu_buffer_size; i++)
				fm_data[fm_offset+i] = gzu_buffer[i];

			free(gzu_buffer);
		}
		else{
			// The file is either a GYM, or an uncompressed VGM
			fseek(ym_log_file, 0, SEEK_END);
			fm_size += (ftell(ym_log_file)+4);
			if(fm_data == 0)
				fm_data = (char *)malloc(fm_size);
			else
				fm_data = (char *)realloc(fm_data, fm_size);
			rewind(ym_log_file);
			fread(&fm_data[fm_offset], 1, fm_size-4-fm_offset, ym_log_file);
			fclose(ym_log_file);
		}
	}

	fm_offset = fm_size-4;
	fm_data[fm_offset] = 0xFF;
	fm_data[fm_offset+1] = 'E';
	fm_data[fm_offset+2] = 'O';
	fm_data[fm_offset+3] = 'F';
	fm_offset += 4;
}

void LoadPCM(unsigned char file, const char *string, char stereo, char repeat){
	#ifdef ALLEGRO_DOS
	allegro_message("Loading PCM file \"%s\"...\n", string);
	#endif

	if(string[0] == '\0'){
		pcm_data_address[file] = 0;
		pcm_data_format_stereo[file] = 0;
		pcm_data_repeat[file] = 0;
		pcm_size += 4;
		if(pcm_data == 0)
			pcm_data = (char *)malloc(pcm_size);
		else
			pcm_data = (char *)realloc(pcm_data, pcm_size);
	}
	else{
		ym_log_file = fopen(string, "rb");
		if(ym_log_file == NULL){
			allegro_message("Could not open file \"%s\"...\n", string);
			return;
		}
		pcm_data_address[file] = pcm_size;
		pcm_data_format_stereo[file] = stereo;
		pcm_data_repeat[file] = repeat;
		fseek(ym_log_file, 0, SEEK_END);
		pcm_size += (ftell(ym_log_file)+4);
		if(pcm_data == 0)
			pcm_data = (char *)malloc(pcm_size);
		else
			pcm_data = (char *)realloc(pcm_data, pcm_size);
		rewind(ym_log_file);
		fread(&pcm_data[pcm_offset], 1, pcm_size-4-pcm_offset, ym_log_file);
		fclose(ym_log_file);
	}

	pcm_offset = pcm_size-4;
	pcm_data[pcm_offset] = 0xFF;
	pcm_data[pcm_offset+1] = 'E';
	pcm_data[pcm_offset+2] = 'O';
	pcm_data[pcm_offset+3] = 'F';
	pcm_offset += 4;
}



void PlayFM(short sound, char buffer, char mix){
	fm_fader[buffer] = 0;
	fm_fade_out[buffer] = 0;
	fm_reset[buffer] = 1;
	fm_track_number[buffer] = sound;

	fm_data_pos[buffer] = fm_data_address[sound];
	fm_data_pos_start[buffer] = fm_data_address[sound];
	fm_data_ch_repeat[buffer] = fm_data_repeat[sound];
	fm_data_ch_type[buffer] = fm_data_type[sound];
	if(fm_data_ch_type[buffer]){	// VGM file
	 	fm_data_pos_loop[buffer] = fm_data[fm_data_pos[buffer] + 0x1C]
		 + (fm_data[fm_data_pos[buffer] + 0x1D] << 8)
		 + (fm_data[fm_data_pos[buffer] + 0x1E] << 16)
		 + (fm_data[fm_data_pos[buffer] + 0x1F] << 24);
	 	fm_data_pos[buffer] += 0x40;
	}
}



void PlayPCM(short sound, char buffer, char mix){
	if(buffer < 0){
		switch(mix){
			case 1:
				// LEFT
				if(pcm_data_poll[0][1]){
					if(pcm_data_poll[0][2]){
						if(pcm_data_poll[0][3]){
							pcm_data_pos[0][1] = pcm_data_address[sound] + 0x2C;
							pcm_data_poll[0][1] = sound;
							pcm_data_ch_repeat[0][1] = pcm_data_repeat[sound];
							pcm_data_pos_start[0][1] = pcm_data_address[sound];
							pcm_data_ch_format_stereo[0][1] = pcm_data_format_stereo[sound];
						}
						else{
							pcm_data_pos[0][3] = pcm_data_address[sound] + 0x2C;
							pcm_data_poll[0][3] = sound;
							pcm_data_ch_repeat[0][3] = pcm_data_repeat[sound];
							pcm_data_pos_start[0][3] = pcm_data_address[sound];
							pcm_data_ch_format_stereo[0][3] = pcm_data_format_stereo[sound];
						}
					}
					else{
						pcm_data_pos[0][2] = pcm_data_address[sound] + 0x2C;
						pcm_data_poll[0][2] = sound;
						pcm_data_ch_repeat[0][2] = pcm_data_repeat[sound];
						pcm_data_pos_start[0][2] = pcm_data_address[sound];
						pcm_data_ch_format_stereo[0][2] = pcm_data_format_stereo[sound];
					}
				}
				else{
					pcm_data_pos[0][1] = pcm_data_address[sound] + 0x2C;
					pcm_data_poll[0][1] = sound;
					pcm_data_ch_repeat[0][1] = pcm_data_repeat[sound];
					pcm_data_pos_start[0][1] = pcm_data_address[sound];
					pcm_data_ch_format_stereo[0][1] = pcm_data_format_stereo[sound];
				}
				break;

			case 2:
				// RIGHT
				if(pcm_data_poll[1][1]){
					if(pcm_data_poll[1][2]){
						if(pcm_data_poll[1][2]){
							pcm_data_pos[1][1] = pcm_data_address[sound] + 0x2C;
							pcm_data_poll[1][1] = sound;
							pcm_data_ch_repeat[1][1] = pcm_data_repeat[sound];
							pcm_data_pos_start[1][1] = pcm_data_address[sound];
							pcm_data_ch_format_stereo[1][1] = pcm_data_format_stereo[sound];
						}
						else{
							pcm_data_pos[1][3] = pcm_data_address[sound] + 0x2C;
							pcm_data_poll[1][3] = sound;
							pcm_data_ch_repeat[1][3] = pcm_data_repeat[sound];
							pcm_data_pos_start[1][3] = pcm_data_address[sound];
							pcm_data_ch_format_stereo[1][3] = pcm_data_format_stereo[sound];
						}
					}
					else{
						pcm_data_pos[1][2] = pcm_data_address[sound] + 0x2C;
						pcm_data_poll[1][2] = sound;
						pcm_data_ch_repeat[1][2] = pcm_data_repeat[sound];
						pcm_data_pos_start[1][2] = pcm_data_address[sound];
						pcm_data_ch_format_stereo[1][2] = pcm_data_format_stereo[sound];
					}
				}
				else{
					pcm_data_pos[1][1] = pcm_data_address[sound] + 0x2C;
					pcm_data_poll[1][1] = sound;
					pcm_data_ch_repeat[1][1] = pcm_data_repeat[sound];
					pcm_data_pos_start[1][1] = pcm_data_address[sound];
					pcm_data_ch_format_stereo[1][1] = pcm_data_format_stereo[sound];
				}
				break;

			default:
				// STEREO
				if(pcm_data_poll[0][1] | pcm_data_poll[1][1]){
					if(pcm_data_poll[0][2] | pcm_data_poll[1][2]){
						if(pcm_data_poll[0][3] | pcm_data_poll[1][3]){
							pcm_data_pos[0][1] = pcm_data_address[sound] + 0x2C;
							pcm_data_pos[1][1] = pcm_data_address[sound] + 0x2C;
							pcm_data_poll[0][1] = sound;
							pcm_data_poll[1][1] = sound;
							pcm_data_ch_repeat[0][1] = pcm_data_repeat[sound];
							pcm_data_ch_repeat[1][1] = pcm_data_repeat[sound];
							pcm_data_pos_start[0][1] = pcm_data_address[sound];
							pcm_data_pos_start[1][1] = pcm_data_address[sound];
							pcm_data_ch_format_stereo[0][1] = pcm_data_format_stereo[sound];
							pcm_data_ch_format_stereo[1][1] = pcm_data_format_stereo[sound];
						}
						else{
							pcm_data_pos[0][3] = pcm_data_address[sound] + 0x2C;
							pcm_data_pos[1][3] = pcm_data_address[sound] + 0x2C;
							pcm_data_poll[0][3] = sound;
							pcm_data_poll[1][3] = sound;
							pcm_data_ch_repeat[0][3] = pcm_data_repeat[sound];
							pcm_data_ch_repeat[1][3] = pcm_data_repeat[sound];
							pcm_data_pos_start[0][3] = pcm_data_address[sound];
							pcm_data_pos_start[1][3] = pcm_data_address[sound];
							pcm_data_ch_format_stereo[0][3] = pcm_data_format_stereo[sound];
							pcm_data_ch_format_stereo[1][3] = pcm_data_format_stereo[sound];
						}
					}
					else{
						pcm_data_pos[0][2] = pcm_data_address[sound] + 0x2C;
						pcm_data_pos[1][2] = pcm_data_address[sound] + 0x2C;
						pcm_data_poll[0][2] = sound;
						pcm_data_poll[1][2] = sound;
						pcm_data_ch_repeat[0][2] = pcm_data_repeat[sound];
						pcm_data_ch_repeat[1][2] = pcm_data_repeat[sound];
						pcm_data_pos_start[0][2] = pcm_data_address[sound];
						pcm_data_pos_start[1][2] = pcm_data_address[sound];
						pcm_data_ch_format_stereo[0][2] = pcm_data_format_stereo[sound];
						pcm_data_ch_format_stereo[1][2] = pcm_data_format_stereo[sound];
					}
				}
				else{
					pcm_data_pos[0][1] = pcm_data_address[sound] + 0x2C;
					pcm_data_pos[1][1] = pcm_data_address[sound] + 0x2C;
					pcm_data_poll[0][1] = sound;
					pcm_data_poll[1][1] = sound;
					pcm_data_ch_repeat[0][1] = pcm_data_repeat[sound];
					pcm_data_ch_repeat[1][1] = pcm_data_repeat[sound];
					pcm_data_pos_start[0][1] = pcm_data_address[sound];
					pcm_data_pos_start[1][1] = pcm_data_address[sound];
					pcm_data_ch_format_stereo[0][1] = pcm_data_format_stereo[sound];
					pcm_data_ch_format_stereo[1][1] = pcm_data_format_stereo[sound];
				}
		}
	}
	else{
		switch(mix){
			case 1:
				pcm_data_pos[0][buffer] = pcm_data_address[sound] + 0x2C;
				pcm_data_poll[0][buffer] = sound;
				pcm_data_ch_repeat[0][buffer] = pcm_data_repeat[sound];
				pcm_data_pos_start[0][buffer] = pcm_data_address[sound];
				pcm_data_ch_format_stereo[0][buffer] = pcm_data_format_stereo[sound];
				break;

			case 2:
				pcm_data_pos[1][buffer] = pcm_data_address[sound] + 0x2C;
				pcm_data_poll[1][buffer] = sound;
				pcm_data_ch_repeat[1][buffer] = pcm_data_repeat[sound];
				pcm_data_pos_start[1][buffer] = pcm_data_address[sound];
				pcm_data_ch_format_stereo[1][buffer] = pcm_data_format_stereo[sound];
				break;

			default:
				pcm_data_pos[0][buffer] = pcm_data_address[sound] + 0x2C;
				pcm_data_pos[1][buffer] = pcm_data_address[sound] + 0x2C;
				pcm_data_poll[0][buffer] = sound;
				pcm_data_poll[1][buffer] = sound;
				pcm_data_ch_repeat[0][buffer] = pcm_data_repeat[sound];
				pcm_data_ch_repeat[1][buffer] = pcm_data_repeat[sound];
				pcm_data_pos_start[0][buffer] = pcm_data_address[sound];
				pcm_data_pos_start[1][buffer] = pcm_data_address[sound];
				pcm_data_ch_format_stereo[0][buffer] = pcm_data_format_stereo[sound];
				pcm_data_ch_format_stereo[1][buffer] = pcm_data_format_stereo[sound];
		}
	}
}



#ifdef AUDIO_REC_ENABLED
void RecordPCM(short *sample_data){
	int i;
	short sample1;
	short sample2;

	for(i=0; i < 735; i++){
		sample1 = sample_data[i<<1] ^ 0x8000;
		sample2 = sample_data[(i<<1)+1] ^ 0x8000;
		fwrite(&sample1, 2, 1, pcm_out);
		fwrite(&sample2, 2, 1, pcm_out);
	}
}
#endif



void StreamPCM(char ch, int *pos_left, int *pos_right, unsigned short *poll_left, unsigned short *poll_right, int *buffer_left, int *buffer_right){
	int position_left;
	int position_right;

	position_left = *pos_left;
	position_right = *pos_right;

	for(i=0; i < 735; i++){
		// LEFT STEREO
		if(pcm_data[position_left] == 0xFF && pcm_data[position_left+1] == 'E'
		 && pcm_data[position_left+2] == 'O' && pcm_data[position_left+3] == 'F'){
			if(pcm_data_ch_repeat[0][ch]){
				position_left = pcm_data_pos_start[0][ch] + 0x2C;
				buffer_left[i] = pcm_data[position_left] + (pcm_data[position_left+1] << 8);
				position_left += (2 << pcm_data_ch_format_stereo[0][ch]);
			}
			else{
				*poll_left = 0;
				buffer_left[i] = 0;
				// little optimization to write the end of the buffer with zeros
				if(position_left == position_right){
					*poll_right = 0;
					while(i < 735){
						buffer_left[i] = 0;
						buffer_right[i] = 0;
						i++;
					}
				}
			}
		}
		else{
			buffer_left[i] = pcm_data[position_left] + (pcm_data[position_left+1] << 8);
			position_left += (2 << pcm_data_ch_format_stereo[0][ch]);
		}
		// RIGHT STEREO
		if(pcm_data[position_right] == 0xFF && pcm_data[position_right+1] == 'E'
		 && pcm_data[position_right+2] == 'O' && pcm_data[position_right+3] == 'F'){
			if(pcm_data_ch_repeat[1][ch]){
				position_right = pcm_data_pos_start[1][ch] + 0x2C;
				buffer_right[i] = pcm_data[position_right] + (pcm_data[position_right+1] << 8);
				position_right += (2 << pcm_data_ch_format_stereo[1][ch]);
			}
			else{
				*poll_right = 0;
				buffer_right[i] = 0;
			}
		}
		else{
			buffer_right[i] = pcm_data[position_right+(2*pcm_data_ch_format_stereo[1][ch])] + (pcm_data[position_right+1+(2*pcm_data_ch_format_stereo[1][ch])] << 8);
			position_right += (2 << pcm_data_ch_format_stereo[1][ch]);
		}
	}

	*pos_left = position_left;
	*pos_right = position_right;
}



void StreamFM(char ch, int *pos, int *buffer_left, int *buffer_right){
	int position;
	short byte;
	//FILE *bgm;
	//FILE *sfx;

	int tempbuf[2][735];
	int *tempbufptr[2];
	int tempdac;
	//int sound_vol;
	int i;
	char n;

	tempbufptr[0] = tempbuf[0];
	tempbufptr[1] = tempbuf[1];
	memset(tempbuf, 0, 2*735*sizeof(int));	// Erase tempbuf

	position = *pos;

	if(bgm_enable){
		return;
	}

	if(fm_reset[ch] == 1){
		fm_reset[ch] = 0;

		// Silence YM2612 and PSG
		YM2612_Write(0, 0x28);
		YM2612_Write(1, 0x00);	// Channel 1
		YM2612_Write(1, 0x01);	// Channel 2
		YM2612_Write(1, 0x02);	// Channel 3
		YM2612_Write(1, 0x04);	// Channel 4
		YM2612_Write(1, 0x05);	// Channel 5
		YM2612_Write(1, 0x06);	// Channel 6
		PSG_Write(0x9F);	// Channel 1
		PSG_Write(0xBF);	// Channel 2
		PSG_Write(0xDF);	// Channel 3
		PSG_Write(0xFF);	// Channel 4 (noise)

		// Set DAC enable bit according to the track setting
		YM2612_Write(0, 0x2B);
		YM2612_Write(1, fm_data_dac[fm_track_number[ch]]);
	}

	if(fm_fade_out[ch]){
		if(fm_fader[ch] == 0){
			BackupVolume();		// PSG
			for(i=0; i < 6; i++){
				BackupTL(i);		// YM2612
				YM2612_Write(0, 0x2B);
				YM2612_Write(1, 0);
			}
		}
		else if(fm_fader[ch] >> 2){
			FadeVolumePSG(fm_fader[ch] >> 2);
			for(i=0; i < 6; i++){
				n = GetALG(i);
				switch(n){
					case 7:
						FadeVolumeYM2612(i, 0, fm_fader[ch] >> 2);
					case 6:
					case 5:
						FadeVolumeYM2612(i, 2, fm_fader[ch] >> 2);
					case 4:
						FadeVolumeYM2612(i, 1, fm_fader[ch] >> 2);
					case 3:
					case 2:
					case 1:
					case 0:
						FadeVolumeYM2612(i, 3, fm_fader[ch] >> 2);
				}
			}
		}
	}

	if(fm_data_ch_type[ch] == FM_TYPE_VGM){
		// VGM file
		i=0;
		while(i < 735){
			if(fm_data_wait[ch] > 0){
				while(fm_data_wait[ch] > 0 && i < 735){
					fm_data_wait[ch]--;

					PSG_Update(tempbufptr, 1);
					YM2612_Update(tempbufptr, 1);
					tempdac = (signed short)(fm_data_sample[ch]<<8);

					*tempbufptr[0] = (float)(*tempbufptr[0]) * FM_VOLUME;
					*tempbufptr[1] = (float)(*tempbufptr[1]) * FM_VOLUME;
					tempdac = (float)(tempdac) * DAC_VOLUME * GetDAC(ch);

					*tempbufptr[0] += tempdac;
					*tempbufptr[1] += tempdac;

					tempbufptr[0]++;
					tempbufptr[1]++;

					i++;
				}
			}
			else{
				byte = fm_data[position];
				switch(byte){
					case 0x4F:	// Game Gear PSG stereo
						// we simply ignore this
						position += 2;
						break;
					case 0x50:	// PSG write
						PSG_Write(fm_data[position+1]);
						position += 2;
						break;
					case 0x52:	// YM2612 port 0 write
						if(fm_data[position+1] == 0x2A){
							// Write to the DAC
							fm_data_sample[ch] = (fm_data[position+2]^0x80);
						}
						else{
							YM2612_Write(0, fm_data[position+1]);
							YM2612_Write(1, fm_data[position+2]);
						}
						position += 3;
						break;
					case 0x53:	// YM2612 port 1 write
						YM2612_Write(2, fm_data[position+1]);
						YM2612_Write(3, fm_data[position+2]);
						position += 3;
						break;
					case 0x61:	// Wait n samples (16-bit)
						fm_data_wait[ch] = fm_data[position+1] + (fm_data[position+2] << 8);
						position += 3;
						break;
					case 0x62:	// Wait 735 samples
						fm_data_wait[ch] = 735;
						position++;
						break;
					case 0x63:	// Wait 882 samples
						fm_data_wait[ch] = 882;
						position++;
						break;
					case 0x66:	// EOF
						if(fm_data_ch_repeat[ch]){
							if(fm_data_pos_loop[ch] != 0){
								position = fm_data_pos_start[ch] + fm_data_pos_loop[ch] + 0x1C;
								fm_data_pos_dac[ch] = fm_data_address_dac[ch];
							}
							else
								position = fm_data_pos_start[ch] + 0x40;
							i++;
						}
						else{
							position = 0;
							fm_data_ch_type[ch] = -1;
							for(; i < 735; i++){
								tempbuf[0][i] = 0;
								tempbuf[1][i] = 0;
							}
						}
						break;
					case 0x67:	// Initialize data block
						fm_data_address_dac[ch] = position + 7;
						position += (7 + fm_data[position+3] + (fm_data[position+4] << 8)
						 + (fm_data[position+5] << 16) + (fm_data[position+6] << 24));
						fm_data_pos_dac[ch] = fm_data_address_dac[ch];
						break;
					case 0xE0:	// Data block seek
						fm_data_pos_dac[ch] = fm_data_address_dac[ch] + fm_data[position+1] + (fm_data[position+2] << 8)
						 + (fm_data[position+3] << 16) + (fm_data[position+4] << 24);
						position += 5;
						break;
					default:
						if(byte >= 0x70 && byte < 0x80){
							// Wait n+1 samples (4-bit)
							fm_data_wait[ch] = (byte & 0xF) + 1;
							position++;
							break;
						}
						if(byte >= 0x80 && byte < 0x90){
							// YM2612 DAC write / wait n samples (4-bit)
							fm_data_sample[ch] = (fm_data[fm_data_pos_dac[ch]]^0x80);
							fm_data_pos_dac[ch]++;
							fm_data_wait[ch] = (byte & 0xF);
							position++;
							break;
						}
						allegro_message("ERROR: Unsupported VGM data 0x%02X found at 0x%06X", byte, position);
						i++;
						rest(250);
						position++;
				}
			}
		}

		for(i=0; i < 735; i++){
			buffer_left[i] = tempbuf[0][i];
			buffer_right[i] = tempbuf[1][i];
		}
	}
	else if(fm_data_ch_type[ch] == FM_TYPE_GYM){
		// GYM file
		byte = fm_data[position];
		position++;
		if(byte == 0xFF && fm_data[position] == 'E' &&
		 fm_data[position+1] == 'O' && fm_data[position+2] == 'F'){
			if(fm_data_ch_repeat[ch]){
				for(i = 255; i >= 0; i--){
					if(position+3 == fm_data_address[i+1]){
						position = fm_data_address[i];
						i = 0;
					}
				}
				byte = fm_data[position];
				position++;
			}
			else{
				position = 0;
				fm_data_ch_type[ch] = -1;
				for(i=0; i < 735; i++){
					tempbuf[0][i] = 0;
					tempbuf[1][i] = 0;
				}
			}
		}
		while(byte > 0){
			switch(byte){
				case 1:
					YM2612_Write(0, fm_data[position]);
					YM2612_Write(1, fm_data[position+1]);
					position += 2;
					break;
				case 2:
					YM2612_Write(2, fm_data[position]);
					YM2612_Write(3, fm_data[position+1]);
					position += 2;
					break;
				case 3:
					PSG_Write(fm_data[position]);
					position++;
			}

			byte = fm_data[position];
			position++;
		}

		PSG_Update(tempbufptr, 735);
		YM2612_Update(tempbufptr, 735);

		for(i=0; i < 735; i++){
			buffer_left[i] = (float)(tempbuf[0][i]) * FM_VOLUME;
			buffer_right[i] = (float)(tempbuf[1][i]) * FM_VOLUME;
		}
	}
	else{
		for(i=0; i < 735; i++){
			buffer_left[i] = 0;
			buffer_right[i] = 0;
		}
		return;
	}

	*pos = position;
}
////////// END ---- * SOUND STUFF * //////////



void LoadScripts(){
	FILE *file_procode;
	int script_end;				// used for PROCODE scripts
	short data_counter;			// used for PROCODE scripts
	short data_order[256+8];	// used for PROCODE scripts
	char game_pc_file[1451];

	free(script);
	script_size = 0;
	for(i=0; i < 256; i++)
		obj_script_size[i] = 0;

	//////////// MOTOROLA FILES ////////////

	//LoadBinary(0x03, "pswap.bin");		// *
		LoadBinary(0x06, "cscrew.bin");		// Emerald Hill / Metropolis
		LoadBinary(0x0D, "signpost.bin");	// *
		LoadBinary(0x11, "bridge.bin");		// Emerald Hill / Hidden Palace
		LoadBinary(0x1B, "speed.bin");		// Chemical Plant
	//LoadBinary(0x1E, "spintube.bin");	// Chemical Plant
	//LoadBinary(0x25, "ring.bin");		// *
		LoadBinary(0x3F, "fan.bin");		// Oil Ocean
		LoadBinary(0x79, "starpost.bin");	// *

	//////////// PROCODE FILES ////////////

	// This is code that can be used for a "static" executable
	//file_procode = fopen(program_filename, "rb");
	//fseek(file_procode, -1451, SEEK_END);

	#ifdef ALLEGRO_DOS
	allegro_message("Loading ProCode script file \"game.pcs\"...\n");
	#endif

	file_procode = fopen("game.pcs", "rb");
	if(file_procode == NULL){
		allegro_message("Could not open file \"game.pcs\". Program closing...\n");
		quit(0);
	}
	fread(&i, 4, 1, file_procode);
	fread(&n, 4, 1, file_procode);
	// add checks for "PROCODE" string
	// add checks for version byte

	data_counter = 0;
	fread(&script_end, 4, 1, file_procode);

	while(data_counter >= 0){
		data_order[data_counter] = 0;	// DO NOT DELETE THIS LINE!
		data_order[data_counter] = getc(file_procode);
		if(data_order[data_counter] != 0){	// pointer for an object
			fread(&i, 4, 1, file_procode);
			obj_script_type[data_order[data_counter]] = SCRIPT_TYPE_PROCODE;
			obj_script_start[data_order[data_counter]] = script_size + i;

			// set size for previous object
			if(data_counter > 0){
				if(data_order[data_counter-1] > 255){
					game_script_size[data_order[data_counter-1] >> 8] =
					 obj_script_start[data_order[data_counter]] - game_script_start[data_order[data_counter-1] >> 8];
				}
				else{
					obj_script_size[data_order[data_counter-1]] =
					 obj_script_start[data_order[data_counter]] - obj_script_start[data_order[data_counter-1]];
				}
			}
			data_counter++;
		}
		else{	// pointer for a generic function
			data_order[data_counter] = getc(file_procode);
			data_order[data_counter] <<= 8;
			if(data_order[data_counter] == 0){	// end of header
				game_script_start[data_order[data_counter] >> 8] = script_size + script_end;

				// set size for previous object
				if(data_counter > 0){
					if(data_order[data_counter-1] > 255){
						game_script_size[data_order[data_counter-1] >> 8] =
						 game_script_start[data_order[data_counter] >> 8] - game_script_start[data_order[data_counter-1] >> 8];
					}
					else{
						obj_script_size[data_order[data_counter-1]] =
						 game_script_start[data_order[data_counter] >> 8] - obj_script_start[data_order[data_counter-1]];
					}
				}
				data_counter = -1;
			}
			else{
				fread(&i, 4, 1, file_procode);
				game_script_start[data_order[data_counter] >> 8] = script_size + i;

				// set size for previous object
				if(data_counter > 0){
					if(data_order[data_counter-1] > 255){
						game_script_size[data_order[data_counter-1] >> 8] =
						 game_script_start[data_order[data_counter] >> 8] - game_script_start[data_order[data_counter-1] >> 8];
					}
					else{
						obj_script_size[data_order[data_counter-1]] =
						 game_script_start[data_order[data_counter] >> 8] - obj_script_start[data_order[data_counter-1]];
					}
				}
				data_counter++;
			}
		}
	}

	script = (char*)realloc(script, script_size + script_end);
	for(i = script_size; i < (script_size + script_end); i++)
		script[i] = getc(file_procode);
	fclose(file_procode);

	script_size = i;



	internal_gvar_ptrs[0] = (int *)&a0;
	internal_gvar_ptrs[1] = (int *)&a1;
	internal_gvar_ptrs[2] = (int *)&a2;
	internal_gvar_ptrs[3] = (int *)&a3;
	internal_gvar_ptrs[4] = (int *)&a4;
	internal_gvar_ptrs[5] = (int *)&a5;
	internal_gvar_ptrs[6] = (int *)&a6;
	internal_gvar_ptrs[7] = (int *)&a7;
	internal_gvar_ptrs[8] = (int *)&d0;
	internal_gvar_ptrs[9] = (int *)&d1;
	internal_gvar_ptrs[10] = (int *)&d2;
	internal_gvar_ptrs[11] = (int *)&d3;
	internal_gvar_ptrs[12] = (int *)&d4;
	internal_gvar_ptrs[13] = (int *)&d5;
	internal_gvar_ptrs[14] = (int *)&d6;
	internal_gvar_ptrs[15] = (int *)&d7;
	internal_gvar_ptrs[16] = (int *)&mouse_z_previous;
	internal_gvar_ptrs[17] = (int *)&camera_mode;
	internal_gvar_ptrs[18] = (int *)&fade_count;
	internal_gvar_ptrs[19] = (int *)&edit_mode;
	internal_gvar_ptrs[20] = (int *)&edit_selector;
	internal_gvar_ptrs[21] = (int *)&edit_clipboard;
	internal_gvar_ptrs[22] = (int *)&smart_mix;
	internal_gvar_ptrs[23] = (int *)&advance_frame;
	internal_gvar_ptrs[24] = (int *)&Game_paused;
	internal_gvar_ptrs[25] = (int *)&DemoCount;
	internal_gvar_ptrs[26] = (int *)&DemoPosition;
	internal_gvar_ptrs[27] = (int *)&DemoFadeDelay;
	internal_gvar_ptrs[28] = (int *)&zone;
	internal_gvar_ptrs[29] = (int *)&act;
	internal_gvar_ptrs[30] = (int *)&stage;
	internal_gvar_ptrs[31] = (int *)&demo;
	internal_gvar_ptrs[32] = (int *)&demo_mode;
	internal_gvar_ptrs[33] = (int *)&frame_skip;
	internal_gvar_ptrs[34] = (int *)&draw_frame;
	internal_gvar_ptrs[35] = (int *)&screen_resolution_x;
	internal_gvar_ptrs[36] = (int *)&screen_resolution_y;
	internal_gvar_ptrs[37] = (int *)&screen_padding_x;
	internal_gvar_ptrs[38] = (int *)&screen_padding_y;
	internal_gvar_ptrs[39] = (int *)&screen_offset_x;
	internal_gvar_ptrs[40] = (int *)&screen_offset_y;
	internal_gvar_ptrs[41] = (int *)&screen_frame_x;
	internal_gvar_ptrs[42] = (int *)&screen_frame_y;
	internal_gvar_ptrs[43] = (int *)&screen_buffer_x;
	internal_gvar_ptrs[44] = (int *)&screen_buffer_y;
	internal_gvar_ptrs[45] = (int *)&screen_scale_x;
	internal_gvar_ptrs[46] = (int *)&screen_scale_y;
	internal_gvar_ptrs[47] = (int *)&screen_double_x;
	internal_gvar_ptrs[48] = (int *)&screen_double_y;
	//internal_gvar_ptrs[49] = (int *)&screen_interlace_x;
	//internal_gvar_ptrs[50] = (int *)&screen_interlace_y;
	internal_gvar_ptrs[51] = (int *)&vsync_enable;
	internal_gvar_ptrs[52] = (int *)&close_button_pressed;
	internal_gvar_ptrs[53] = (int *)&number_of_objects_in_memory;
	internal_gvar_ptrs[54] = (int *)&ticCounter60L;
	internal_gvar_ptrs[55] = (int *)&fps;
	internal_gvar_ptrs[56] = (int *)&show_fps;
	internal_gvar_ptrs[57] = (int *)&ticCounterL;
	internal_gvar_ptrs[58] = (int *)&ticCounterR;
	internal_gvar_ptrs[59] = (int *)&ticCounter60R;
	internal_gvar_ptrs[60] = (int *)&ticUpdated;
	internal_gvar_ptrs[61] = (int *)&waitCounter;
	//internal_gvar_ptrs[62] = (int *)&frames;
	internal_gvar_ptrs[63] = (int *)&num_of_players;
	internal_gvar_ptrs[64] = (int *)&num_of_frames;
	internal_gvar_ptrs[65] = (int *)&frame;
	internal_gvar_ptrs[66] = (int *)&Level_Inactive_flag;
	internal_gvar_ptrs[67] = (int *)&Timer_frames;
	internal_gvar_ptrs[68] = (int *)&Debug_object;
	internal_gvar_ptrs[69] = (int *)&Debug_placement_mode;
	internal_gvar_ptrs[70] = (int *)&Debug_mode_flag;
	internal_gvar_ptrs[71] = (int *)&Emerald_count;
	internal_gvar_ptrs[72] = (int *)&Continue_count;
	internal_gvar_ptrs[73] = (int *)&Time_Over_flag;
	internal_gvar_ptrs[74] = (int *)&Timer_frames;
	internal_gvar_ptrs[75] = (int *)&Timer_minute_word;
	internal_gvar_ptrs[76] = (int *)&Timer_minute;
	internal_gvar_ptrs[77] = (int *)&Timer_second;
	internal_gvar_ptrs[78] = (int *)&Timer_millisecond;
	internal_gvar_ptrs[79] = (int *)&titlecard_sequence_end;
	internal_gvar_ptrs[80] = (int *)key; // already a pointer by itself
	internal_gvar_ptrs[81] = (int *)&key_shifts;
	internal_gvar_ptrs[82] = (int *)&prompt;
	internal_gvar_ptrs[83] = (int *)&full_screen;
	internal_gvar_ptrs[84] = (int *)&audio_volume;
	internal_gvar_ptrs[85] = (int *)&show_version;
	internal_gvar_ptrs[86] = (int *)&show_watermark;
	internal_gvar_ptrs[87] = (int *)&Update_HUD_lives;
	internal_gvar_ptrs[88] = (int *)&Update_HUD_rings;
	internal_gvar_ptrs[89] = (int *)&Update_HUD_timer;
	internal_gvar_ptrs[90] = (int *)&Update_HUD_score;

	for(i=0; i < 4; i++){
		internal_pvar_ptrs[i][0] = (int *)&player[i].character;
		internal_pvar_ptrs[i][1] = (int *)&player[i].sidekick;
		internal_pvar_ptrs[i][2] = (int *)&player[i].CtrlInput;
		internal_pvar_ptrs[i][3] = (int *)&player[i].render_flags;
		internal_pvar_ptrs[i][4] = (int *)&player[i].art_tile;
		internal_pvar_ptrs[i][5] = (int *)&player[i].x_pos;
		internal_pvar_ptrs[i][6] = (int *)&player[i].x_pos_pre;
		internal_pvar_ptrs[i][7] = (int *)&player[i].y_pos;
		internal_pvar_ptrs[i][8] = (int *)&player[i].y_pos_pre;
		internal_pvar_ptrs[i][9] = (int *)&player[i].x_vel;
		internal_pvar_ptrs[i][10] = (int *)&player[i].y_vel;
		internal_pvar_ptrs[i][11] = (int *)&player[i].inertia;
		internal_pvar_ptrs[i][12] = (int *)&player[i].x_radius;
		internal_pvar_ptrs[i][13] = (int *)&player[i].y_radius;
		internal_pvar_ptrs[i][14] = (int *)&player[i].priority;
		internal_pvar_ptrs[i][15] = (int *)&player[i].anim_frame_duration;
		internal_pvar_ptrs[i][16] = (int *)&player[i].anim_frame;
		internal_pvar_ptrs[i][17] = (int *)&player[i].anim;
		internal_pvar_ptrs[i][18] = (int *)&player[i].next_anim;
		internal_pvar_ptrs[i][19] = (int *)&player[i].status;
		internal_pvar_ptrs[i][20] = (int *)&player[i].routine;
		internal_pvar_ptrs[i][21] = (int *)&player[i].routine_secondary;
		internal_pvar_ptrs[i][22] = (int *)&player[i].angle;
		internal_pvar_ptrs[i][23] = (int *)&player[i].flip_angle;
		internal_pvar_ptrs[i][24] = (int *)&player[i].air_left;
		internal_pvar_ptrs[i][25] = (int *)&player[i].flip_turned;
		internal_pvar_ptrs[i][26] = (int *)&player[i].obj_control;
		internal_pvar_ptrs[i][27] = (int *)&player[i].status_secondary;
		internal_pvar_ptrs[i][28] = (int *)&player[i].flips_remaining;
		internal_pvar_ptrs[i][29] = (int *)&player[i].flip_speed;
		internal_pvar_ptrs[i][30] = (int *)&player[i].move_lock;
		internal_pvar_ptrs[i][31] = (int *)&player[i].invulnerable_time;
		internal_pvar_ptrs[i][32] = (int *)&player[i].invincibility_time;
		internal_pvar_ptrs[i][33] = (int *)&player[i].speedshoes_time;
		internal_pvar_ptrs[i][34] = (int *)&player[i].next_tilt;
		internal_pvar_ptrs[i][35] = (int *)&player[i].tilt;
		internal_pvar_ptrs[i][36] = (int *)&player[i].stick_to_convex;
		internal_pvar_ptrs[i][37] = (int *)&player[i].spindash_flag;
		internal_pvar_ptrs[i][38] = (int *)&player[i].spindash_counter;
		internal_pvar_ptrs[i][39] = (int *)&player[i].jumping;
		internal_pvar_ptrs[i][40] = (int *)&player[i].interact;
		internal_pvar_ptrs[i][41] = (int *)&player[i].layer;
		internal_pvar_ptrs[i][42] = (int *)&player[i].layer_plus;
		internal_pvar_ptrs[i][43] = (int *)&player[i].Sonic_top_speed;
		internal_pvar_ptrs[i][44] = (int *)&player[i].Sonic_acceleration;
		internal_pvar_ptrs[i][45] = (int *)&player[i].Sonic_deceleration;
		internal_pvar_ptrs[i][46] = (int *)&player[i].Ctrl_Held;
		internal_pvar_ptrs[i][47] = (int *)&player[i].Ctrl_Press;
		internal_pvar_ptrs[i][48] = (int *)&player[i].Ctrl_Held_Logical;
		internal_pvar_ptrs[i][49] = (int *)&player[i].Ctrl_Press_Logical;
		internal_pvar_ptrs[i][50] = (int *)&player[i].width_pixels;
		internal_pvar_ptrs[i][51] = (int *)&player[i].Sonic_Look_delay_counter;
		// Sonic_Stat_Record_Buf[128];
		// Sonic_Pos_Record_Buf[128];
		internal_pvar_ptrs[i][52] = (int *)&player[i].Camera_X_pos;
		internal_pvar_ptrs[i][53] = (int *)&player[i].Camera_Y_pos;
		internal_pvar_ptrs[i][54] = (int *)&player[i].Camera_Z_pos;
		internal_pvar_ptrs[i][55] = (int *)&player[i].Camera_Max_Y_pos;
		internal_pvar_ptrs[i][56] = (int *)&player[i].Camera_Min_X_pos;
		internal_pvar_ptrs[i][57] = (int *)&player[i].Camera_Max_X_pos;
		internal_pvar_ptrs[i][58] = (int *)&player[i].Camera_Min_Y_pos;
		internal_pvar_ptrs[i][59] = (int *)&player[i].Camera_Max_Y_pos_now;
		internal_pvar_ptrs[i][60] = (int *)&player[i].Camera_X_pos_coarse;
		internal_pvar_ptrs[i][61] = (int *)&player[i].Sonic_Pos_Record_Index;
		internal_pvar_ptrs[i][62] = (int *)&player[i].Camera_Y_pos_bias;
		internal_pvar_ptrs[i][63] = (int *)&player[i].Ring_count;
		internal_pvar_ptrs[i][64] = (int *)&player[i].Score;
		internal_pvar_ptrs[i][65] = (int *)&player[i].Life_count;
		internal_pvar_ptrs[i][66] = (int *)&player[i].Extra_life_flags;
		internal_pvar_ptrs[i][67] = (int *)&player[i].Last_star_pole_hit;
		internal_pvar_ptrs[i][68] = (int *)&player[i].Saved_Last_star_pole_hit;
		internal_pvar_ptrs[i][69] = (int *)&player[i].Saved_x_pos;
		internal_pvar_ptrs[i][70] = (int *)&player[i].Saved_y_pos;
		internal_pvar_ptrs[i][71] = (int *)&player[i].Saved_Ring_count;
		internal_pvar_ptrs[i][72] = (int *)&player[i].Saved_Timer;
		internal_pvar_ptrs[i][73] = (int *)&player[i].Saved_art_tile;
		internal_pvar_ptrs[i][74] = (int *)&player[i].Saved_layer;
		internal_pvar_ptrs[i][75] = (int *)&player[i].Saved_Camera_X_pos;
		internal_pvar_ptrs[i][76] = (int *)&player[i].Saved_Camera_Y_pos;
		internal_pvar_ptrs[i][77] = (int *)&player[i].Saved_Water_Level;
		internal_pvar_ptrs[i][78] = (int *)&player[i].Saved_Extra_life_flags;
		internal_pvar_ptrs[i][79] = (int *)&player[i].Saved_Extra_life_flags_2P;
		internal_pvar_ptrs[i][80] = (int *)&player[i].Saved_Camera_Max_Y_pos;
		internal_pvar_ptrs[i][81] = (int *)&player[i].Collision_addr;
		internal_pvar_ptrs[i][82] = (int *)&player[i].ram_EEB0;
		internal_pvar_ptrs[i][83] = (int *)&player[i].ram_EEB2;
		internal_pvar_ptrs[i][84] = (int *)&player[i].ram_EEBE;
		internal_pvar_ptrs[i][85] = (int *)&player[i].ram_EED0;
		internal_pvar_ptrs[i][86] = (int *)&player[i].ram_F65C;
		internal_pvar_ptrs[i][87] = (int *)&player[i].ram_F768;
		internal_pvar_ptrs[i][88] = (int *)&player[i].ram_F76A;
		internal_pvar_ptrs[i][89] = (int *)&player[i].ram_F7C7;
	}

	internal_gvar_size[0] = sizeof(a0);
	internal_gvar_size[1] = sizeof(a1);
	internal_gvar_size[2] = sizeof(a2);
	internal_gvar_size[3] = sizeof(a3);
	internal_gvar_size[4] = sizeof(a4);
	internal_gvar_size[5] = sizeof(a5);
	internal_gvar_size[6] = sizeof(a6);
	internal_gvar_size[7] = sizeof(a7);
	internal_gvar_size[8] = sizeof(d0);
	internal_gvar_size[9] = sizeof(d1);
	internal_gvar_size[10] = sizeof(d2);
	internal_gvar_size[11] = sizeof(d3);
	internal_gvar_size[12] = sizeof(d4);
	internal_gvar_size[13] = sizeof(d5);
	internal_gvar_size[14] = sizeof(d6);
	internal_gvar_size[15] = sizeof(d7);
	internal_gvar_size[16] = sizeof(mouse_z_previous);
	internal_gvar_size[17] = sizeof(camera_mode);
	internal_gvar_size[18] = sizeof(fade_count);
	internal_gvar_size[19] = sizeof(edit_mode);
	internal_gvar_size[20] = sizeof(edit_selector);
	internal_gvar_size[21] = sizeof(edit_clipboard);
	internal_gvar_size[22] = sizeof(smart_mix);
	internal_gvar_size[23] = sizeof(advance_frame);
	internal_gvar_size[24] = sizeof(Game_paused);
	internal_gvar_size[25] = sizeof(DemoCount);
	internal_gvar_size[26] = sizeof(DemoPosition);
	internal_gvar_size[27] = sizeof(DemoFadeDelay);
	internal_gvar_size[28] = sizeof(zone);
	internal_gvar_size[29] = sizeof(act);
	internal_gvar_size[30] = sizeof(stage);
	internal_gvar_size[31] = sizeof(demo);
	internal_gvar_size[32] = sizeof(demo_mode);
	internal_gvar_size[33] = sizeof(frame_skip);
	internal_gvar_size[34] = sizeof(draw_frame);
	internal_gvar_size[35] = sizeof(screen_resolution_x);
	internal_gvar_size[36] = sizeof(screen_resolution_y);
	internal_gvar_size[37] = sizeof(screen_padding_x);
	internal_gvar_size[38] = sizeof(screen_padding_y);
	internal_gvar_size[39] = sizeof(screen_offset_x);
	internal_gvar_size[40] = sizeof(screen_offset_y);
	internal_gvar_size[41] = sizeof(screen_frame_x);
	internal_gvar_size[42] = sizeof(screen_frame_y);
	internal_gvar_size[43] = sizeof(screen_buffer_x);
	internal_gvar_size[44] = sizeof(screen_buffer_y);
	internal_gvar_size[45] = sizeof(screen_scale_x);
	internal_gvar_size[46] = sizeof(screen_scale_y);
	internal_gvar_size[47] = sizeof(screen_double_x);
	internal_gvar_size[48] = sizeof(screen_double_y);
	//internal_gvar_size[49] = sizeof(screen_interlace_x);
	//internal_gvar_size[50] = sizeof(screen_interlace_y);
	internal_gvar_size[51] = sizeof(vsync_enable);
	internal_gvar_size[52] = sizeof(close_button_pressed);
	internal_gvar_size[53] = sizeof(number_of_objects_in_memory);
	internal_gvar_size[54] = sizeof(ticCounter60L);
	internal_gvar_size[55] = sizeof(fps);
	internal_gvar_size[56] = sizeof(show_fps);
	internal_gvar_size[57] = sizeof(ticCounterL);
	internal_gvar_size[58] = sizeof(ticCounterR);
	internal_gvar_size[59] = sizeof(ticCounter60R);
	internal_gvar_size[60] = sizeof(ticUpdated);
	internal_gvar_size[61] = sizeof(waitCounter);
	//internal_gvar_size[62] = sizeof(frames);
	internal_gvar_size[63] = sizeof(num_of_players);
	internal_gvar_size[64] = sizeof(num_of_frames);
	internal_gvar_size[65] = sizeof(frame);
	internal_gvar_size[66] = sizeof(Level_Inactive_flag);
	internal_gvar_size[67] = sizeof(Timer_frames);
	internal_gvar_size[68] = sizeof(Debug_object);
	internal_gvar_size[69] = sizeof(Debug_placement_mode);
	internal_gvar_size[70] = sizeof(Debug_mode_flag);
	internal_gvar_size[71] = sizeof(Emerald_count);
	internal_gvar_size[72] = sizeof(Continue_count);
	internal_gvar_size[73] = sizeof(Time_Over_flag);
	internal_gvar_size[74] = sizeof(Timer_frames);
	internal_gvar_size[75] = sizeof(Timer_minute_word);
	internal_gvar_size[76] = sizeof(Timer_minute);
	internal_gvar_size[77] = sizeof(Timer_second);
	internal_gvar_size[78] = sizeof(Timer_millisecond);
	internal_gvar_size[79] = sizeof(titlecard_sequence_end);
	internal_gvar_size[80] = sizeof(key[0]);
	internal_gvar_size[81] = sizeof(key_shifts);
	internal_gvar_size[82] = sizeof(prompt);
	internal_gvar_size[83] = sizeof(full_screen);
	internal_gvar_size[84] = sizeof(audio_volume);
	internal_gvar_size[85] = sizeof(show_version);
	internal_gvar_size[86] = sizeof(show_watermark);
	internal_gvar_size[87] = sizeof(Update_HUD_lives);
	internal_gvar_size[88] = sizeof(Update_HUD_rings);
	internal_gvar_size[89] = sizeof(Update_HUD_timer);
	internal_gvar_size[90] = sizeof(Update_HUD_score);

	internal_ovar_size[0] = 4;
	internal_ovar_size[1] = 1;
	internal_ovar_size[2] = 1;
	internal_ovar_size[3] = 1;
	internal_ovar_size[4] = 1;
	internal_ovar_size[5] = 1;
	internal_ovar_size[6] = 1;
	internal_ovar_size[7] = 2;
	internal_ovar_size[8] = 2;
	internal_ovar_size[9] = 1;
	internal_ovar_size[10] = 1;
	internal_ovar_size[11] = 1;
	internal_ovar_size[12] = 1;
	internal_ovar_size[13] = 1;
	internal_ovar_size[14] = 1;
	internal_ovar_size[15] = 1;
	internal_ovar_size[16] = 2;
	internal_ovar_size[17] = 1;
	internal_ovar_size[18] = 2;
	internal_ovar_size[19] = 2;
	internal_ovar_size[20] = 2;
	internal_ovar_size[21] = 2;
	internal_ovar_size[22] = 1;
	internal_ovar_size[23] = 1;

	internal_pvar_size[0] = sizeof(player[i].character);
	internal_pvar_size[1] = sizeof(player[i].sidekick);
	internal_pvar_size[2] = sizeof(player[i].CtrlInput);
	internal_pvar_size[3] = sizeof(player[i].render_flags);
	internal_pvar_size[4] = sizeof(player[i].art_tile);
	internal_pvar_size[5] = sizeof(player[i].x_pos);
	internal_pvar_size[6] = sizeof(player[i].x_pos_pre);
	internal_pvar_size[7] = sizeof(player[i].y_pos);
	internal_pvar_size[8] = sizeof(player[i].y_pos_pre);
	internal_pvar_size[9] = sizeof(player[i].x_vel);
	internal_pvar_size[10] = sizeof(player[i].y_vel);
	internal_pvar_size[11] = sizeof(player[i].inertia);
	internal_pvar_size[12] = sizeof(player[i].x_radius);
	internal_pvar_size[13] = sizeof(player[i].y_radius);
	internal_pvar_size[14] = sizeof(player[i].priority);
	internal_pvar_size[15] = sizeof(player[i].anim_frame_duration);
	internal_pvar_size[16] = sizeof(player[i].anim_frame);
	internal_pvar_size[17] = sizeof(player[i].anim);
	internal_pvar_size[18] = sizeof(player[i].next_anim);
	internal_pvar_size[19] = sizeof(player[i].status);
	internal_pvar_size[20] = sizeof(player[i].routine);
	internal_pvar_size[21] = sizeof(player[i].routine_secondary);
	internal_pvar_size[22] = sizeof(player[i].angle);
	internal_pvar_size[23] = sizeof(player[i].flip_angle);
	internal_pvar_size[24] = sizeof(player[i].air_left);
	internal_pvar_size[25] = sizeof(player[i].flip_turned);
	internal_pvar_size[26] = sizeof(player[i].obj_control);
	internal_pvar_size[27] = sizeof(player[i].status_secondary);
	internal_pvar_size[28] = sizeof(player[i].flips_remaining);
	internal_pvar_size[29] = sizeof(player[i].flip_speed);
	internal_pvar_size[30] = sizeof(player[i].move_lock);
	internal_pvar_size[31] = sizeof(player[i].invulnerable_time);
	internal_pvar_size[32] = sizeof(player[i].invincibility_time);
	internal_pvar_size[33] = sizeof(player[i].speedshoes_time);
	internal_pvar_size[34] = sizeof(player[i].next_tilt);
	internal_pvar_size[35] = sizeof(player[i].tilt);
	internal_pvar_size[36] = sizeof(player[i].stick_to_convex);
	internal_pvar_size[37] = sizeof(player[i].spindash_flag);
	internal_pvar_size[38] = sizeof(player[i].spindash_counter);
	internal_pvar_size[39] = sizeof(player[i].jumping);
	internal_pvar_size[40] = sizeof(player[i].interact);
	internal_pvar_size[41] = sizeof(player[i].layer);
	internal_pvar_size[42] = sizeof(player[i].layer_plus);
	internal_pvar_size[43] = sizeof(player[i].Sonic_top_speed);
	internal_pvar_size[44] = sizeof(player[i].Sonic_acceleration);
	internal_pvar_size[45] = sizeof(player[i].Sonic_deceleration);
	internal_pvar_size[46] = sizeof(player[i].Ctrl_Held);
	internal_pvar_size[47] = sizeof(player[i].Ctrl_Press);
	internal_pvar_size[48] = sizeof(player[i].Ctrl_Held_Logical);
	internal_pvar_size[49] = sizeof(player[i].Ctrl_Press_Logical);
	internal_pvar_size[50] = sizeof(player[i].width_pixels);
	internal_pvar_size[51] = sizeof(player[i].Sonic_Look_delay_counter);
	internal_pvar_size[52] = sizeof(player[i].Camera_X_pos);
	internal_pvar_size[53] = sizeof(player[i].Camera_Y_pos);
	internal_pvar_size[54] = sizeof(player[i].Camera_Z_pos);
	internal_pvar_size[55] = sizeof(player[i].Camera_Max_Y_pos);
	internal_pvar_size[56] = sizeof(player[i].Camera_Min_X_pos);
	internal_pvar_size[57] = sizeof(player[i].Camera_Max_X_pos);
	internal_pvar_size[58] = sizeof(player[i].Camera_Min_Y_pos);
	internal_pvar_size[59] = sizeof(player[i].Camera_Max_Y_pos_now);
	internal_pvar_size[60] = sizeof(player[i].Camera_X_pos_coarse);
	internal_pvar_size[61] = sizeof(player[i].Sonic_Pos_Record_Index);
	internal_pvar_size[62] = sizeof(player[i].Camera_Y_pos_bias);
	internal_pvar_size[63] = sizeof(player[i].Ring_count);
	internal_pvar_size[64] = sizeof(player[i].Score);
	internal_pvar_size[65] = sizeof(player[i].Life_count);
	internal_pvar_size[66] = sizeof(player[i].Extra_life_flags);
	internal_pvar_size[67] = sizeof(player[i].Last_star_pole_hit);
	internal_pvar_size[68] = sizeof(player[i].Saved_Last_star_pole_hit);
	internal_pvar_size[69] = sizeof(player[i].Saved_x_pos);
	internal_pvar_size[70] = sizeof(player[i].Saved_y_pos);
	internal_pvar_size[71] = sizeof(player[i].Saved_Ring_count);
	internal_pvar_size[72] = sizeof(player[i].Saved_Timer);
	internal_pvar_size[73] = sizeof(player[i].Saved_art_tile);
	internal_pvar_size[74] = sizeof(player[i].Saved_layer);
	internal_pvar_size[75] = sizeof(player[i].Saved_Camera_X_pos);
	internal_pvar_size[76] = sizeof(player[i].Saved_Camera_Y_pos);
	internal_pvar_size[77] = sizeof(player[i].Saved_Water_Level);
	internal_pvar_size[78] = sizeof(player[i].Saved_Extra_life_flags);
	internal_pvar_size[79] = sizeof(player[i].Saved_Extra_life_flags_2P);
	internal_pvar_size[80] = sizeof(player[i].Saved_Camera_Max_Y_pos);
	internal_pvar_size[81] = sizeof(player[i].Collision_addr);
	internal_pvar_size[82] = sizeof(player[i].ram_EEB0);
	internal_pvar_size[83] = sizeof(player[i].ram_EEB2);
	internal_pvar_size[84] = sizeof(player[i].ram_EEBE);
	internal_pvar_size[85] = sizeof(player[i].ram_EED0);
	internal_pvar_size[86] = sizeof(player[i].ram_F65C);
	internal_pvar_size[87] = sizeof(player[i].ram_F768);
	internal_pvar_size[88] = sizeof(player[i].ram_F76A);
	internal_pvar_size[89] = sizeof(player[i].ram_F7C7);
}



#ifdef ENABLE_LOGGING
void TabLog(signed char tab){
	logtab += tab;
	if(logtab < 0)
		logtab = 0;
}

void WriteLog(const char *string, ...){
	if(logline < 100000){
		msglog_tabs[logline] = logtab;
		strncpy(msglog[logline], string, 32);
		msglog[logline][31] = '\0';
		logline++;
	}
}

void RamLog(unsigned short addr){
	char string[32];
	for(i=0; i < 64 && logline < 100000; i += 8){
		sprintf(
			string, "\t%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X",
			ram_68k[0xFFFF - addr - i-0], ram_68k[0xFFFF - addr - i-1],
			ram_68k[0xFFFF - addr - i-2], ram_68k[0xFFFF - addr - i-3],
			ram_68k[0xFFFF - addr - i-4], ram_68k[0xFFFF - addr - i-5],
			ram_68k[0xFFFF - addr - i-6], ram_68k[0xFFFF - addr - i-7]
		);

		if(string[0] == '\t'){
			strncpy(msglog[logline], string, 32);
			msglog[logline][31] = '\0';
			logline++;
		}
	}
}

void VarLog(short var){
	char string[32];
	if(logline < 100000){
		switch(var){
			// 68000 REGISTERS
			case REG_A0:
				sprintf(string, "a0 = 0x%08X", a0);
				break;
			case REG_A1:
				sprintf(string, "a1 = 0x%08X", a1);
				break;
			case REG_A2:
				sprintf(string, "a2 = 0x%08X", a2);
				break;
			case REG_A3:
				sprintf(string, "a3 = 0x%08X", a3);
				break;
			case REG_A4:
				sprintf(string, "a4 = 0x%08X", a4);
				break;
			case REG_A5:
				sprintf(string, "a5 = 0x%08X", a5);
				break;
			case REG_A6:
				sprintf(string, "a6 = 0x%08X", a6);
				break;
			case REG_A7:
				sprintf(string, "a7 = 0x%08X", a7);
				break;
			case REG_D0:
				sprintf(string, "d0 = 0x%08X", d0);
				break;
			case REG_D1:
				sprintf(string, "d1 = 0x%08X", d1);
				break;
			case REG_D2:
				sprintf(string, "d2 = 0x%08X", d2);
				break;
			case REG_D3:
				sprintf(string, "d3 = 0x%08X", d3);
				break;
			case REG_D4:
				sprintf(string, "d4 = 0x%08X", d4);
				break;
			case REG_D5:
				sprintf(string, "d5 = 0x%08X", d5);
				break;
			case REG_D6:
				sprintf(string, "d6 = 0x%08X", d6);
				break;
			case REG_D7:
				sprintf(string, "d7 = 0x%08X", d7);
				break;
			case REG_PC:
				sprintf(string, "pc = 0x%08X", pc);
				break;
			case REG_SR:
				sprintf(string, "sr = 0x%08X", sr);
				break;

			// VARIABLES
			case VAR_VAR_TEMP:
				sprintf(string, "var_temp = 0x%08X", var_temp);
				break;
			case VAR_ROUTINE:
				sprintf(string, "routine = 0x%02X", p->routine);
				break;
			case VAR_X_RADIUS:
				sprintf(string, "x_radius = 0x%02X", p->x_radius);
				break;
			case VAR_Y_RADIUS:
				sprintf(string, "y_radius = 0x%02X", p->y_radius);
				break;
			case VAR_X_POS:
				sprintf(string, "x_pos = 0x%04X", p->x_pos);
				break;
			case VAR_X_POS_PRE:
				sprintf(string, "x_pos_pre = 0x%04X", p->x_pos_pre);
				break;
			case VAR_Y_POS:
				sprintf(string, "y_pos = 0x%04X", p->y_pos);
				break;
			case VAR_Y_POS_PRE:
				sprintf(string, "y_pos_pre = 0x%04X", p->y_pos_pre);
				break;
			case VAR_X_VEL:
				sprintf(string, "x_vel = 0x%04X", p->x_vel);
				break;
			case VAR_Y_VEL:
				sprintf(string, "y_vel = 0x%04X", p->y_vel);
				break;
			case VAR_INERTIA:
				sprintf(string, "inertia = 0x%04X", p->inertia);
				break;
			case VAR_STATUS:
				sprintf(string, "status = 0x%02X", p->status);
				break;
			case VAR_ANGLE:
				sprintf(string, "angle = 0x%02X", p->angle);
				break;
			case VAR_ANIM:
				sprintf(string, "anim = 0x%02X", p->anim);
				break;
			case VAR_LAYER:
				sprintf(string, "layer = 0x%02X", p->layer);
				break;
			default:
				return;
		}

		msglog_tabs[logline] = logtab;
		msglog[logline][0] = '*';
		msglog[logline][1] = ' ';
		strncpy(&msglog[logline][2], string, 30);
		msglog[logline][31] = '\0';
		logline++;
	}
}

void RegLog(){
	WriteLog("*****************");
	VarLog(REG_A0);
	VarLog(REG_A1);
	VarLog(REG_A2);
	VarLog(REG_A3);
	VarLog(REG_A4);
	VarLog(REG_A5);
	VarLog(REG_A6);
	VarLog(REG_A7);
	VarLog(REG_D0);
	VarLog(REG_D1);
	VarLog(REG_D2);
	VarLog(REG_D3);
	VarLog(REG_D4);
	VarLog(REG_D5);
	VarLog(REG_D6);
	VarLog(REG_D7);
	WriteLog("*****************");
}

void DumpLog(const char *string, ...){
	int tab_space = 0x20202020;
	short endofline = 0x0A0D;
	short logy;
	char logx;
	FILE *dump;

	dump = fopen(string, "w+b");
	for(logy=0; logy < logline; logy++){
		for(logx=0; logx < msglog_tabs[logy]; logx++)
			fwrite(&tab_space, 4, 1, dump);
		for(logx=0; logx < 32; logx++){
			if(msglog[logy][logx] == '\0'){
				fwrite(&endofline, 2, 1, dump);
				break;
			}
			fwrite(&msglog[logy][logx], 1, 1, dump);
		}
	}
	fclose(dump);
}

void ResetLog(){
	logline = 0;
	logtab = 0;
}



#ifdef ENABLE_SRAM_LOG_ANALYSIS
void AnalyzeSRAM(){
	int i;
	int problem;

	unsigned char prosonic_ram[64] = {
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	};

	unsigned char ram_skip_table[64] = {
		// 9 means it should NOT be skipped, but is due to issues
		1, 9, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9
	};

	unsigned char ram_conversion_table[64] = {
		0x00, 0x01,		0x03, 0x02,		0x04, 0x05,		0x06, 0x07,
		0x09, 0x08,		0x0B, 0x0A,		0x0D, 0x0C,		0x0F, 0x0E,
		0x11, 0x10,		0x13, 0x12,		0x15, 0x14,		0x16, 0x17,
		0x18, 0x19,		0x1A, 0x1B,		0x1C, 0x1D,		0x1E, 0x1F,
		0x20, 0x21,		0x22, 0x23,		0x24, 0x25,		0x26, 0x27,
		0x28, 0x29,		0x2A, 0x2B,		0x2C, 0x2D,		0x2F, 0x2E,
		0x31, 0x30,		0x33, 0x32,		0x35, 0x34,		0x36, 0x37,
		0x38, 0x39,		0x3B, 0x3A,		0x3C, 0x3D,		0x3E, 0x3F
	};

	char number_string_conversion[] = "0123456789ABCDEF";
	char sram_frame_string[] = "sram_frame[0x00] = 0x00";
	char prosonic_ram_string[] = "prosonic_ram[0x00] = 0x00";

	prosonic_ram[0x01] = p->render_flags;	// bit 1 always off (broken), turn off
	*(short *)&prosonic_ram[0x02] = p->art_tile;
	*(short *)&prosonic_ram[0x08] = p->x_pos;
	*(short *)&prosonic_ram[0x0A] = p->x_pos_pre;
	*(short *)&prosonic_ram[0x0C] = p->y_pos;
	*(short *)&prosonic_ram[0x0E] = p->y_pos_pre;
	*(short *)&prosonic_ram[0x10] = p->x_vel;
	*(short *)&prosonic_ram[0x12] = p->y_vel;
	*(short *)&prosonic_ram[0x14] = p->inertia;
	prosonic_ram[0x16] = p->y_radius;
	prosonic_ram[0x17] = p->x_radius;
	prosonic_ram[0x18] = p->priority;
	prosonic_ram[0x1B] = p->anim_frame;
	prosonic_ram[0x1C] = p->anim;
	prosonic_ram[0x1D] = p->next_anim;
	prosonic_ram[0x1E] = p->anim_frame_duration;
	prosonic_ram[0x22] = p->status;
	prosonic_ram[0x24] = p->routine;
	prosonic_ram[0x25] = p->routine_secondary;
	prosonic_ram[0x26] = p->angle;
	prosonic_ram[0x27] = p->flip_angle;
	prosonic_ram[0x28] = p->air_left;
	prosonic_ram[0x29] = p->flip_turned;
	prosonic_ram[0x2A] = p->obj_control;
	prosonic_ram[0x2B] = p->status_secondary;
	prosonic_ram[0x2C] = p->flips_remaining;
	prosonic_ram[0x2D] = p->flip_speed;
	*(short *)&prosonic_ram[0x2E] = p->move_lock;
	*(short *)&prosonic_ram[0x30] = p->invulnerable_time;
	*(short *)&prosonic_ram[0x32] = p->invincibility_time;
	*(short *)&prosonic_ram[0x34] = p->speedshoes_time;
	prosonic_ram[0x36] = p->next_tilt;
	prosonic_ram[0x37] = p->tilt;
	prosonic_ram[0x38] = p->stick_to_convex;
	prosonic_ram[0x39] = p->spindash_flag;
	*(short *)&prosonic_ram[0x3A] = p->spindash_counter;
	prosonic_ram[0x3C] = p->jumping;
	prosonic_ram[0x3D] = p->interact;		// not yet used, turn off
	prosonic_ram[0x3E] = p->layer - 4;		// not quite right, turn off
	prosonic_ram[0x3F] = p->layer_plus - 4;	// not quite right, turn off

	problem = 0;
	for(i=0; i < 64; i++){
		if(!ram_skip_table[i]){
			n = ram_conversion_table[i];
			if(sram_frame[i] != prosonic_ram[n]){
				//allegro_message(
				//"sram_frame[0x%02X] = 0x%02X\nprosonic_ram[0x%02X] = 0x%02X",
				//i, sram_frame[i], i, prosonic_ram[n]);
				prosonic_ram_string[0x0F] = number_string_conversion[n >> 4];
				prosonic_ram_string[0x10] = number_string_conversion[n & 0xF];
				prosonic_ram_string[0x17] = number_string_conversion[prosonic_ram[n] >> 4];
				prosonic_ram_string[0x18] = number_string_conversion[prosonic_ram[n] & 0xF];
				sram_frame_string[0x0D] = number_string_conversion[i >> 4];
				sram_frame_string[0x0E] = number_string_conversion[i & 0xF];
				sram_frame_string[0x15] = number_string_conversion[sram_frame[i] >> 4];
				sram_frame_string[0x16] = number_string_conversion[sram_frame[i] & 0xF];
				WriteLog("\n");
				if(!problem){
					WriteLog("CALCULATION PROBLEMS:");
					WriteLog("\n");
				}
				WriteLog(prosonic_ram_string);
				WriteLog(sram_frame_string);
				problem++;
			}
		}
	}

	if(problem){
		DumpLog("obj01.log");
		if(problem == 1)
			allegro_message("A problem was discovered with 1 byte at frame 0x%X. A log file has been dumped.", ticCounterL);
		else
			allegro_message("Problems were discovered with %i bytes at frame 0x%X. A log file has been dumped.", problem, ticCounterL);
		rest(100);
	}
}
#endif
#endif


/*
void LoadSavestate(const char *string, ...){
	unsigned char ram[64];
	int i;
	FILE *savestate;
	savestate = fopen(string, "rb");
	if(savestate == NULL){
		allegro_message("Cannot open savestate \"%s\".", string);
		return;
	}
	fseek(savestate, 0xD478, SEEK_SET);

	for(i = 0; i < 64; i++)
		fread(&ram[i], 1, 1, savestate);

	p->render_flags = ram[1];
	p->art_tile = (ram[2] << 8) + ram[3];
	p->x_pos = (ram[8] << 8) + ram[9];
	p->x_pos_pre = (ram[0xA] << 8) + ram[0xB];
	p->y_pos = (ram[0xC] << 8) + ram[0xD];
	p->y_pos_pre = (ram[0xE] << 8) + ram[0xF];
	p->x_vel = (ram[0x10] << 8) + ram[0x11];
	p->y_vel = (ram[0x12] << 8) + ram[0x13];
	p->inertia = (ram[0x14] << 8) + ram[0x15];
	p->y_radius = ram[0x16];
	p->x_radius = ram[0x17];
	p->priority = ram[0x18];
	p->anim_frame = ram[0x1B];
	p->anim = ram[0x1C];
	p->next_anim = ram[0x1D];
	//p->anim_frame_duration = ram[0x1E];
	p->status = ram[0x22];
	p->routine = ram[0x24];
	p->routine_secondary = ram[0x25];
	p->angle = ram[0x26];
	p->flip_angle = ram[0x27];
	p->air_left = ram[0x28];
	p->flip_turned = ram[0x29];
	p->obj_control = ram[0x2A];
	p->status_secondary = ram[0x2B];
	p->flips_remaining = ram[0x2C];
	p->flip_speed = ram[0x2D];
	p->move_lock = (ram[0x2E] << 8) + ram[0x2F];
	p->invulnerable_time = (ram[0x30] << 8) + ram[0x31];
	p->invincibility_time = (ram[0x32] << 8) + ram[0x33];
	p->speedshoes_time = (ram[0x34] << 8) + ram[0x35];
	p->next_tilt = ram[0x36];
	p->tilt = ram[0x37];
	p->stick_to_convex = ram[0x38];
	p->spindash_flag = ram[0x39];
	p->spindash_counter = (ram[0x3A] << 8) + ram[0x3B];
	p->jumping = ram[0x3C];
	p->interact = ram[0x3D];
	p->layer = ram[0x3E] + 4;
	p->layer_plus = ram[0x3F] + 4;

	fclose(savestate);
}
*/


void FadeRGB(BITMAP *b){
	unsigned short x;
	unsigned short y;
	int color_r;
	int color_g;
	int color_b;
	int new_color_r;
	int new_color_g;
	int new_color_b;

	if(fade_count < 0){ // Fade to black
		for(y=0; y < screen_resolution_y; y++){
			for(x=16; x < screen_resolution_x+16; x++){
				color_r = (unsigned short)(b->line[y][(x<<1)+1]<<8) + (unsigned short)(b->line[y][x<<1]);
				color_g = (color_r >> 6) & 31;
				color_b = color_r & 31;
				color_r >>= 11;

				// Fade color R
				if(color_r + fade_count <= 0)
					new_color_r = 0;
				else
					new_color_r = color_r + fade_count;

				if(new_color_r == 0){
					// Fade color G
					if(color_g + color_r + fade_count <= 0)
						new_color_g = 0;
					else
						new_color_g = color_g + color_r + fade_count;

					if(new_color_g == 0){
						// Fade color B
						if(color_b + color_g + color_r + fade_count <= 0)
							new_color_b = 0;
						else
							new_color_b = color_b + color_g + color_r + fade_count;
					}
					else
						new_color_b = color_b;
				}
				else{
					new_color_g = color_g;
					new_color_b = color_b;
				}

				b->line[y][(x<<1)+1] = (new_color_r << 3) + (new_color_g >> 2);
				b->line[y][x<<1] = (new_color_g << 6) + new_color_b;
			}
		}
	}
	else{ // Fade to white
		for(y=0; y < screen_resolution_y; y++){
			for(x=16; x < screen_resolution_x+16; x++){
				color_r = (unsigned short)(b->line[y][(x<<1)+1]<<8) + (unsigned short)(b->line[y][x<<1]);
				color_g = (color_r >> 6) & 31;
				color_b = color_r & 31;
				color_r >>= 11;

				// Fade color R
				if(color_r + fade_count >= 31)
					new_color_r = 31;
				else
					new_color_r = color_r + fade_count;

				if(new_color_r == 31){
					// Fade color G
					if(color_g + fade_count - (31-color_r) >= 31)
						new_color_g = 31;
					else
						new_color_g = color_g + fade_count - (31-color_r);

					if(new_color_g == 31){
						// Fade color B
						if(color_b + fade_count - (31-color_r) + (31-color_g) >= 31)
							new_color_b = 31;
						else
							new_color_b = color_b + fade_count - (31-color_r) + (31-color_g);
					}
					else
						new_color_b = color_b;
				}
				else{
					new_color_g = color_g;
					new_color_b = color_b;
				}

				b->line[y][(x<<1)+1] = (new_color_r << 3) + (new_color_g >> 2);
				b->line[y][x<<1] = (new_color_g << 6) + new_color_b;
			}
		}
	}
}



void TitleCard(){
	int addr;
	int tc_size;
	int i;
	int n;

	if(titlecard_sequence_end)	return;

	if(titlecard_counter[0] == 0xFF){
		titlecard_file = fopen("tcard.dat", "rb");
		if(titlecard_file == NULL){
			allegro_message("Could not open file \"tcard.dat\". Program closing...\n");
			quit(0);
		}
		fseek(titlecard_file, 0, SEEK_END);
		tc_size = ftell(titlecard_file);
		rewind(titlecard_file);

		if(titlecard_data) free(titlecard_data);
		titlecard_data = (char *)malloc(tc_size);
		fread(titlecard_data, 1, tc_size, titlecard_file);
		fclose(titlecard_file);

		for(i=0; i < titlecard_data[0]; i++){
			titlecard_phase[i] = -1;
			titlecard_counter[i] = 0;
			titlecard_xpos[i] = (titlecard_data[(i<<3)+5] << 8) + titlecard_data[(i<<3)+6];
			titlecard_ypos[i] = (titlecard_data[(i<<3)+7] << 8) + titlecard_data[(i<<3)+8];
		}
		if(titlecard_data[0] > 0)
			fade_count = titlecard_xpos[0];

		Update_HUD_timer = 0;
	}

	for(i=0; i < titlecard_data[0]; i++){
		if(titlecard_counter[i] != 0xFF){
			addr = (titlecard_data[(i<<3)+1] << 8) + titlecard_data[(i<<3)+2];
			if(i == 0){
				if(titlecard_counter[i] == 0){
					titlecard_phase[i]++;
					titlecard_counter[i] = titlecard_data[addr+(titlecard_phase[i]*2)];
				}
				if(titlecard_counter[i] != 0xFF){
					titlecard_counter[i]--;
					titlecard_xpos[i] += (signed char)titlecard_data[addr+(titlecard_phase[i]*2)+1];
					fade_count = titlecard_xpos[i];
				}
			}
			else{
				if(titlecard_counter[i] == 0){
					titlecard_phase[i]++;
					titlecard_counter[i] = titlecard_data[addr+(titlecard_phase[i]*3)];
				}
				if(titlecard_counter[i] != 0xFF){
					titlecard_counter[i]--;
					titlecard_xpos[i] += (signed char)titlecard_data[addr+(titlecard_phase[i]*3)+1];
					titlecard_ypos[i] += (signed char)titlecard_data[addr+(titlecard_phase[i]*3)+2];
					switch(num_of_frames){
						case 1:
							DrawSprite(&z, LayerB[frame-1], 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
							break;
						case 2:
							DrawSprite(&z, LayerM, 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
							DrawSprite(&z, LayerM, 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
							break;
						case 3:
							DrawSprite(&z, LayerM, 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
							DrawSprite(&z, LayerM, 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
							DrawSprite(&z, LayerM, 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
							break;
						case 4:
							DrawSprite(&z, LayerM, 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
							DrawSprite(&z, LayerM, 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
							DrawSprite(&z, LayerM, 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
							DrawSprite(&z, LayerM, 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
					}
				}
			}
		}
	}

	n=0;
	for(i=0; i < titlecard_data[0]; i++)
		n += titlecard_counter[i];
	if(titlecard_data[0] * 0xFF == n){
		titlecard_sequence_end = 1;
		free(titlecard_data);
		titlecard_data = 0; // DO NOT DELETE ME!! Prevents memory leaks!
		ticCounterL = 0;
		Update_HUD_timer = 1;
	}
}



void UpdateBlockLayout(ZONE *z){
	// This function is used with the editing tools so any changes made to the
	// level layout will automatically update the COMPILED_MAP array.

	unsigned short x;
	unsigned short y;

	if(!COMPILED_MAP)
		return;

	for(y=0; y < blocks_y; y++){
		for(x=0; x < blocks_x; x++)
			COMPILED_MAP[(y * blocks_x) + x] = ReadBlock(z, x<<4, y<<4);
	}
}



void CreateBlockLayout(ZONE *z){
	// This function maps out all the 16x16 blocks into an array so they can be
	// quickly referenced without having to read the tile layout. It uses extra
	// memory, but it provides a performance boost.

	unsigned short x;
	unsigned short y;

	if(COMPILED_MAP) free(COMPILED_MAP);
	COMPILED_MAP = malloc(blocks_x * blocks_y * 4);

	for(y=0; y < blocks_y; y++){
		for(x=0; x < blocks_x; x++)
			COMPILED_MAP[(y * blocks_x) + x] = ReadBlock(z, x<<4, y<<4);
	}

	if(COMPILED_BACKMAP) free(COMPILED_BACKMAP);
	COMPILED_BACKMAP = malloc(blocks_x * blocks_y * 2);

	for(y=0; y < blocks_y; y++){
		for(x=0; x < blocks_x; x++)
			COMPILED_BACKMAP[(y * blocks_x) + x] = ReadBackBlock(z, x<<4, y<<4);
	}
}



int ReadBlock(ZONE *z, unsigned short x, unsigned short y){
	unsigned short t;
	unsigned int b;
	// BMAP FORMAT
	// ////////uuuuuuus bptttttt fmBBBBBB BBBBBBBB OLD FORMAT!
	// ttttttuu uuuLbpBP fmNNNNNN NNNNNNNN
	// (NOTE: little endian!!)
	//
	//
	// (N) block number
	// (m) mirror
	// (f) flip
	// (P) platform (layer 1)
	// (B) barrier (layer 1)
	// (p) platform (layer 2)
	// (b) barrier (layer 2)
	// (L) plane
	// (u) undefined
	// (t) translucency

	x %= ((z->TMAP[tmap_ptr-2]+1) << (5+tilesize));
	y %= ((z->TMAP[tmap_ptr-1]+1) << (5+tilesize));

	t = ((x / (32 << tilesize)) + (y / (32 << tilesize) * tiles_x_long)) << 1;
	t = *(short *)(&z->TMAP[tmap_ptr+t]);

	b = (((x & ((32 << tilesize) - 1)) >> 4) + (((y & ((32 << tilesize) - 1)) >> 4) * (2 << tilesize))) << 2;
	b = *(int *)(&z->BMAP[bmap_ptr + b + ((((2 << tilesize) * (2 << tilesize)) << 2) * t)]);

	return b;
}



short ReadBackBlock(ZONE *z, unsigned short x, unsigned short y){
	unsigned short t;
	unsigned int b;
	// BMAP FORMAT
	// ////////uuuuuuus bptttttt fmBBBBBB BBBBBBBB OLD FORMAT!
	// ttttttuu uuuLbpBP fmNNNNNN NNNNNNNN
	// (NOTE: little endian!!)
	//
	//
	// (N) block number
	// (m) mirror
	// (f) flip
	// (P) platform (layer 1)
	// (B) barrier (layer 1)
	// (p) platform (layer 2)
	// (b) barrier (layer 2)
	// (L) plane
	// (u) undefined
	// (t) translucency

	x %= ((z->TMAP[tmap_ptr-2]+1) << (5+tilesize));
	y %= ((z->TMAP[tmap_ptr-1]+1) << (5+tilesize));

	x += (blocks_x<<4);

	t = ((x / (32 << tilesize)) + (y / (32 << tilesize) * tiles_x_long)) << 1;
	t = *(short *)(&z->TMAP[tmap_ptr+t]);

	b = (((x & ((32 << tilesize) - 1)) >> 4) + (((y & ((32 << tilesize) - 1)) >> 4) * (2 << tilesize))) << 2;
	b = *(short *)(&z->BMAP[bmap_ptr + b + ((((2 << tilesize) * (2 << tilesize)) << 2) * t)]);
	return b;
}



int Pause(){
	if(p->Life_count == 0){
		Game_paused = 0;
		return 0;
	}

	if(p->Ctrl_Press & 0x80){
		Game_paused ^= 1;
		return Game_paused;
	}

	if(Game_paused){
		#ifdef ENABLE_LOGGING
		if(p->Ctrl_Press & 0x40){
			DumpLog("obj01.log");
		}
		#endif
		if(p->Ctrl_Held & 0x10){
			if(p->Ctrl_Press & 0x10)
				p->Ctrl_Press ^= 0x10;
			advance_frame ^= 1;
			return advance_frame ^ 1;
		}
		if(p->Ctrl_Press & 0x20){
			p->Ctrl_Press ^= 0x20;
			advance_frame ^= 1;
			return advance_frame ^ 1;
		}
		if(advance_frame == 1)
			advance_frame = 0;
	}

	return Game_paused;
}



int main(int argc, char **argv)
{
	// Copy program filename
	i = strlen(argv[0]) + 1;
	program_filename = (char *)malloc(i);
	strcpy(program_filename, argv[0]);

	// Copy IP address
	#ifndef ALLEGRO_DOS
	if(argc > 1)
		strcpy(ip_input, argv[1]);
	else
		strcpy(ip_input, "127.0.0.1");
	#endif

	// Required //
	#ifdef ALLEGRO_DOS
	printf("Initializing Allegro...\n");
	#endif
	if(allegro_init() != 0){
		#ifdef ALLEGRO_DOS
		printf("Error: allegro_init() has failed!");
		#endif
		return 0;
	}

	#ifdef ALLEGRO_DOS
	allegro_message("Installing keyboard...\n");
	#endif
	if(install_keyboard() != 0){
		allegro_message("Error: install_keyboard() has failed!");
		allegro_exit();
		return 0;
	}

	#ifdef ALLEGRO_DOS
	allegro_message("Installing timer...\n");
	#endif
	if(install_timer() != 0){
		allegro_message("Error: install_timer() has failed!");
		allegro_exit();
		return 0;
	}

	#ifndef ALLEGRO_DOS
	if(nlInit() == NL_FALSE){
		allegro_message("Error: nlInit() has failed!");
		allegro_exit();
		return 0;
	}
	nlSelectNetwork(NL_IP);
	#endif

	#ifdef ENABLE_MOUSE
	#ifdef ALLEGRO_DOS
	allegro_message("Installing mouse...\n");
	#endif
	if(install_mouse() < 0){	// positive numbers and zero both pass
		allegro_message("Warning: install_mouse() has failed!");
	}
	#endif



	p = &player[0];



	// Just to be safe //
	for(i=0; i<4; i++){
		LayerB[i] = 0;
		LayerL[i] = 0;
		LayerH[i] = 0;
		LayerSH[i] = 0;
	}
	LayerM = 0;



	// Fix for Mac port (must create window before using dialog boxes)
	#ifndef ALLEGRO_DOS
	set_color_depth(16);
	screen_resolution_x = 320;
	screen_resolution_y = 224;
	screen_padding_x = 0;
	screen_padding_y = 16;
	screen_double_x = 0;
	screen_double_y = 0;
	full_screen = 0;
	screen_offset_x = screen_padding_x >> 1;
	screen_offset_y = screen_padding_y >> 1;
	SetGfxModeWindow();
	#endif



	#ifdef ALLEGRO_DOS
	system("CLS");
	allegro_message("\nSelect number of players:\n\n"
					"Press 1 for single player\n"
					"Press 2 for two players\n"
					"Press 3 for three players\n"
					"Press 4 for four players\n");
	#else
	allegro_message("Click OK, and then select number of players:\n\n"
					"Press 1 for single player\n"
					"Press 2 for two players\n"
					"Press 3 for three players\n"
					"Press 4 for four players\n");
	#endif

	while(!key[KEY_1] && !key[KEY_2] && !key[KEY_3] && !key[KEY_4]);
	if(key[KEY_1])	num_of_frames = 1;
	if(key[KEY_2])	num_of_frames = 2;
	if(key[KEY_3])	num_of_frames = 3;
	if(key[KEY_4])	num_of_frames = 4;



	set_config_file("setup.cfg");

	strcpy(config_section[0], "1_player");
	strcpy(config_section[1], "2_player");
	strcpy(config_section[2], "3_player");
	strcpy(config_section[3], "4_player");

	#ifdef ALLEGRO_DOS
	allegro_message("\nReading configuration file...\n");
	#endif

	#ifdef ALLEGRO_LINUX
	// We want it to default to "DIGI_ESD" on Linux due to issues with PulseAudio
	digi_card = get_config_int(config_section[num_of_frames-1], "digi_card", DIGI_ESD);
	#else
	digi_card = get_config_int(config_section[num_of_frames-1], "digi_card", DIGI_AUTODETECT);
	#endif

	audio_volume = get_config_int(config_section[num_of_frames-1], "audio_volume", 0x1F);
	smart_mix = get_config_int(config_section[num_of_frames-1], "smart_mix", 1);
	vsync_enable = get_config_int(config_section[num_of_frames-1], "vsync_enable", 0);
	frame_skip = get_config_int(config_section[num_of_frames-1], "frame_skip", 0);
	screen_resolution_x = get_config_int(config_section[num_of_frames-1], "screen_resolution_x", 320);
	screen_resolution_y = get_config_int(config_section[num_of_frames-1], "screen_resolution_y", 224);
	screen_padding_x = get_config_int(config_section[num_of_frames-1], "screen_padding_x", 0);
	screen_padding_y = get_config_int(config_section[num_of_frames-1], "screen_padding_y", 16);
	screen_double_x = get_config_int(config_section[num_of_frames-1], "screen_double_x", 0);
	screen_double_y = get_config_int(config_section[num_of_frames-1], "screen_double_y", 0);
	full_screen = get_config_int(config_section[num_of_frames-1], "full_screen", 0);
	key_bind[0].bind_up = get_config_int(config_section[0], "bind_up", 84);
	key_bind[0].bind_down = get_config_int(config_section[0], "bind_down", 85);
	key_bind[0].bind_left = get_config_int(config_section[0], "bind_left", 82);
	key_bind[0].bind_right = get_config_int(config_section[0], "bind_right", 83);
	key_bind[0].bind_b = get_config_int(config_section[0], "bind_b", 19);
	key_bind[0].bind_c = get_config_int(config_section[0], "bind_c", 4);
	key_bind[0].bind_a = get_config_int(config_section[0], "bind_a", 1);
	key_bind[0].bind_start = get_config_int(config_section[0], "bind_start", 67);
	for(i=1; i < 4; i++){
		key_bind[i].bind_up = get_config_int(config_section[i], "bind_up", 0);
		key_bind[i].bind_down = get_config_int(config_section[i], "bind_down", 0);
		key_bind[i].bind_left = get_config_int(config_section[i], "bind_left", 0);
		key_bind[i].bind_right = get_config_int(config_section[i], "bind_right", 0);
		key_bind[i].bind_b = get_config_int(config_section[i], "bind_b", 0);
		key_bind[i].bind_c = get_config_int(config_section[i], "bind_c", 0);
		key_bind[i].bind_a = get_config_int(config_section[i], "bind_a", 0);
		key_bind[i].bind_start = get_config_int(config_section[i], "bind_start", 0);
	}



	// Optional //
	#ifdef ALLEGRO_DOS
	allegro_message("Installing sound...\n");
	#endif
	if(install_sound(digi_card, MIDI_NONE, NULL) != 0){
		allegro_message("Warning: install_sound() has failed!");
		#ifdef ALLEGRO_DOS
		allegro_message("\n");
		#endif
	}
	set_volume(255, 0);
	set_volume_per_voice(0);
	audio_compression = 0x10;	// 1:1 ratio
	//audio_volume = 0x1F;



	// Initialize network variables
	#ifndef ALLEGRO_DOS
	sock_server = NL_INVALID;
	sock_client = NL_INVALID;
	game_socket[0] = NL_INVALID;
	game_socket[1] = NL_INVALID;
	game_socket[2] = NL_INVALID;
	game_socket[3] = NL_INVALID;
	prosonicnet = NL_INVALID;
	port = 727;
	#endif
	num_of_messages = 4;
	message_time_limit = 10 * 60;	// 10 seconds
	message = (char *)malloc(64 * (num_of_messages+1));		// +1 for overhead
	message_time = (short *)malloc(2 * (num_of_messages+1));
	for(i=0; i <= num_of_messages; i++)
		message_time[i] = 0;

	#ifndef ALLEGRO_DOS
	/*if(num_of_frames > 1){
		allegro_message("Select multiplayer mode:\n\n"
						"Press 0 for offline\n"
						"Press 1 for netplay (server)\n"
						"Press 2 for netplay (client)\n");
		while(!key[KEY_0] && !key[KEY_1] && !key[KEY_2]);
		if(key[KEY_0]){ net_type = NET_TYPE_OFFLINE; }
		else{
			if(key[KEY_1]){ net_type = NET_TYPE_SERVER; serverside_setup(); }
			if(key[KEY_2]){	net_type = NET_TYPE_CLIENT; clientside_setup(); }
		}
	}*/
	#endif



	fm_offset = 0;
	fm_size = 0;
	fm_data = 0;

	fm_data_pos[0] = 0;
	fm_data_pos[1] = 0;
	fm_data_pos[2] = 0;
	fm_data_pos[3] = 0;

	LoadFM(0x00, "", 0, 0, 0);
	LoadFM(0x01, "ehz.vgz", FM_TYPE_VGM, FM_DAC_ON, FM_REPEAT_ON);
	LoadFM(0x02, "mcz2.vgz", FM_TYPE_VGM, FM_DAC_ON, FM_REPEAT_ON);
	LoadFM(0x03, "launch.vgz", FM_TYPE_VGM, FM_DAC_ON, FM_REPEAT_ON);
	LoadFM(0x04, "crystegg.vgz", FM_TYPE_VGM, FM_DAC_ON, FM_REPEAT_ON);
	LoadFM(0x1A, "endofact.vgz", FM_TYPE_VGM, FM_DAC_ON, FM_REPEAT_OFF);
	LoadFM(0x20, "jump.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF);
	LoadFM(0x21, "starpost.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF);
	LoadFM(0x23, "pain.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF);
	LoadFM(0x24, "brake.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF);
	LoadFM(0x3C, "blastoff.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF);
	LoadFM(0x3E, "spin.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF);
	LoadFM(0x4C, "spring.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF);
	LoadFM(0x4F, "sign1.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF); // signpost spin
	LoadFM(0x53, "starpost.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF); // signpost spin (2P)
	LoadFM(0x60, "spindash.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF);



	pcm_offset = 0;
	pcm_size = 0;
	pcm_data = 0;

	pcm_data_pos[0][0] = 0;
	pcm_data_pos[0][1] = 0;
	pcm_data_pos[0][2] = 0;
	pcm_data_pos[0][3] = 0;
	pcm_data_pos[1][0] = 0;
	pcm_data_pos[1][1] = 0;
	pcm_data_pos[1][2] = 0;
	pcm_data_pos[1][3] = 0;

	LoadPCM(0x00, "", 0, 0);
	LoadPCM(0x35, "ring.wav", PCM_TYPE_MONO, PCM_REPEAT_OFF);



	#ifdef ALLEGRO_DOS
	allegro_message("Initializing YM2612...\n");
	#endif
	YM2612_Init(7670453, 44100, 1 );
	#ifdef ALLEGRO_DOS
	allegro_message("Initializing PSG...\n");
	#endif
	PSG_Init(3579545, 44100);
	stream = play_audio_stream(735, 16, 1, 44100, 255, 128);
	if(!stream) quit(0);
	BackupYM2612(0);
	BackupYM2612(1);
	BackupYM2612(2);
	BackupYM2612(3);
	BackupPSG(0);
	BackupPSG(1);
	BackupPSG(2);
	BackupPSG(3);

	mix_volume = 0;

	#ifdef AUDIO_REC_ENABLED
		pcm_out = fopen("output.wav", "wb");
		i = 0x46464952;		// "RIFF"
		fwrite(&i, 4, 1, pcm_out);
		i = 0;				// reserved for (size - 0x8)
		fwrite(&i, 4, 1, pcm_out);
		i = 0x45564157;		// "WAVE"
		fwrite(&i, 4, 1, pcm_out);
		i = 0x20746D66;		// "fmt "
		fwrite(&i, 4, 1, pcm_out);
		i = 0x10;
		fwrite(&i, 4, 1, pcm_out);
		i = 0x20001;
		fwrite(&i, 4, 1, pcm_out);
		i = 0xAC44;
		fwrite(&i, 4, 1, pcm_out);
		i = 0x2B110;
		fwrite(&i, 4, 1, pcm_out);
		i = 0x100004;
		fwrite(&i, 4, 1, pcm_out);
		i = 0x61746164;		// "data"
		fwrite(&i, 4, 1, pcm_out);
		i = 0;				// reserved for (size - 0x2C)
		fwrite(&i, 4, 1, pcm_out);
	#endif



	LoadFont0();

	InitLevelSelect();

	SpriteTable = 0;	// Needed to prevent memory leaks
	SpriteTableSize = 0;

	LoadSprite(0x101, "url.psf");
	LoadSprite(0x102, "panel.psf");
	LoadSprite(0x103, "tcard.psf");
	LoadSprite(0x104, "font1.psf");
	LoadSprite(0x105, "hud.psf");

	LoadSprite(0x00, "sonic.psf");
	LoadSprite(0x11, "sonic.psf");
	LoadSprite(0x25, "ring.psf");

	LoadScripts();



	for(i=0; i < 10; i++){
		strcpy(demo_file_list[i], "demo");
		demo_file_list[i][4] = i + 0x30;
		strcpy(&demo_file_list[i][5], ".psd");
		#ifdef ENABLE_SRAM_LOG_ANALYSIS
			strcpy(sram_file_list[i], "demo");
			sram_file_list[i][4] = i + 0x30;
			strcpy(&sram_file_list[i][5], ".srm");
		#endif
	}
	demo_number = 0;
	demo_mode = 0;
	DemoPosition = 0;
	DemoFile = NULL;
	DemoFadeDelay = 3;	// "3" for Sonic 1-2, "1" for Sonic 3



	frame = 1;

	ticCounter60L = 0;	// Logic
	ticCounterR = 0;	// Real-time
	ticCounter60R = 0;	// Real-time
	ticUpdated = 0;
	LOCK_VARIABLE(ticUpdated);
	LOCK_VARIABLE(ticCounterR);
	LOCK_VARIABLE(ticCounter60R);
	LOCK_VARIABLE(ticCounter60L);
	LOCK_VARIABLE(fps);
	LOCK_VARIABLE(waitCounter);
	LOCK_FUNCTION(TicUpdate);
	install_int_ex(&TicUpdate, BPS_TO_TIMER(60));



	set_color_depth(16);
	screen_offset_x = screen_padding_x >> 1;
	screen_offset_y = screen_padding_y >> 1;
	if(full_screen)
		SetGfxModeFull();
	else
		SetGfxModeWindow();

	#ifdef ENABLE_MOUSE
		show_mouse(screen);
	#endif



	close_button_pressed = FALSE;
	#ifndef ALLEGRO_DOS
	LOCK_FUNCTION(close_button_handler);
	set_close_button_callback(close_button_handler);
	#endif

	//LOCK_VARIABLE(fm_data_pos);
	//LOCK_FUNCTION(StreamFM);
	//LOCK_FUNCTION(ProcessSound);
	//install_int_ex(&ProcessSound, BPS_TO_TIMER(60));



	fade_count = -96;
	titlecard_counter[0] = 0xFF;	// required to start TitleCard()

	for(i=0; i < 256; i++){
		strcpy(zone_file_list[i], "zone");
		zone_file_list[i][4] = (i / 100) + 0x30;
		zone_file_list[i][5] = ((i / 10) % 10) + 0x30;
		zone_file_list[i][6] = (i % 10) + 0x30;
		strcpy(&zone_file_list[i][7], ".pzf");
	}

	CreateZone(&z);

	zone = 0;
	act = 0;
	stage = 0;

	Level_Inactive_flag = 1;
	LoadZone(&z, zone_file_list[zone]);
	InitDraw(&z);
	AllocateObjects(&z);
	CreateBlockLayout(&z);

	strcpy(TextBuffer, "ProSonic - ");
	strcat(TextBuffer, (&z)->LevelName1);
	strcat(TextBuffer, " ZONE");
	set_window_title(TextBuffer);



	// SET UP THE CELL DATA
	num_of_cells = z.FILTERCOMPOSITE[0];
	num_of_filters = z.FILTERCOMPOSITE[1];
	cells_ptr = *(short *)&z.FILTERCOMPOSITE[2];
	filters_ptr = *(short *)&z.FILTERCOMPOSITE[4];
	bg_rgb_color = *(short *)&z.FILTERCOMPOSITE[6];
	filter_a = z.FILTERCOMPOSITE[8];
	filter_b = z.FILTERCOMPOSITE[9];
	filter_a_speed = z.FILTERCOMPOSITE[10];
	filter_b_speed = z.FILTERCOMPOSITE[11];

	cell_data = (CELL*)(&z.FILTERCOMPOSITE[cells_ptr]);



	for(i=0; i < 4; i++){
		player[i].status = 0;
		player[i].layer = 0x10;
		player[i].Life_count = 3;

		player[i].x_pos = z.Act[act].Stage[stage].StartX[0];
		player[i].y_pos = z.Act[act].Stage[stage].StartY[0];
		player[i].Camera_X_pos = player[i].x_pos - (screen_resolution_x >> 1);
		player[i].Camera_Y_pos = player[i].y_pos - (screen_resolution_y >> 1);
		player[i].Camera_Min_X_pos = z.Act[act].Stage[stage].LevelXL;
		player[i].Camera_Max_X_pos = z.Act[act].Stage[stage].LevelXR;
		player[i].Camera_Min_Y_pos = z.Act[act].Stage[stage].LevelYT;
		player[i].Camera_Max_Y_pos = z.Act[act].Stage[stage].LevelYB;
		player[i].Camera_Max_Y_pos_now = z.Act[act].Stage[stage].LevelYB;
		player[i].Camera_Y_pos_bias = 0x60;
	}

	water_current_height = 0x24C00;
	water_direction = 1;
	water_accel = 0;
	z.Act[act].Stage[stage].WaterEnable = 0;

	counter_fe74 = 0x80;
	counter_fe74_direction = 1;
	counter_fe74_accel = 0;



	while(!key[KEY_ESC] && !close_button_pressed){
		if(demo_number >= 0){
			if(demo_mode == 0){
				demo_mode = 1;
				DemoFile = NULL;
				for(i=0; i < 10; i++){	// search for next existing demo file
					n = PlayDemo(demo_number);
					if(n == 0)
						i = 9;
					else if(n == -1 && i == 9)
						quit(ERROR_NO_DEMO_FILES);
					else{
						demo_number++;
						demo_number %= 10;
					}
				}
			}
		}
		for(frame=1; frame <= num_of_frames; frame++){
			p = &player[frame-1];
			if(show_menu == 0){
				if(demo_mode != 1 && titlecard_sequence_end){
					p->CtrlInput = 0;
					if(!typing){
						if(key[key_bind[frame-1].bind_up])		p->CtrlInput |= 1;
						if(key[key_bind[frame-1].bind_down])	p->CtrlInput |= 2;
						if(key[key_bind[frame-1].bind_left])	p->CtrlInput |= 4;
						if(key[key_bind[frame-1].bind_right])	p->CtrlInput |= 8;

						if(key[key_bind[frame-1].bind_b])		p->CtrlInput |= 0x10;
						if(key[key_bind[frame-1].bind_c])		p->CtrlInput |= 0x20;
						if(key[key_bind[frame-1].bind_a])		p->CtrlInput |= 0x40;

						if(key[key_bind[frame-1].bind_start])	p->CtrlInput |= 0x80;
					}
				}
				else
					p->CtrlInput = 0;

				if(demo_mode == 1)
					PlayDemo(demo_number);
				else if(demo_mode > 1)
					RecordDemo(9);

				#ifdef ENABLE_FRAME_CONTROL
					while(key[KEY_D]);
					while(!key[KEY_S] && !key[KEY_D]);
				#endif



				if(demo_mode == 1 && key[KEY_ENTER]){
					DemoPosition = 0;
					demo_mode = 0;
					if(demodata)
						free(demodata);
					demodata = 0;
					demo_size = 0;
					demo_number = -1;
					zone = 0;
					act = 0;
					stage = 0;
					Level_Inactive_flag = 1;
				}



				p->Ctrl_Press = 0;
				if(p->CtrlInput & 1){
					if((p->Ctrl_Held & 1) == 0)
						p->Ctrl_Press |= 1;
					p->Ctrl_Held |= 1;
				}
				else
					p->Ctrl_Held &= 0xFE;

				if(p->CtrlInput & 2){
					if((p->Ctrl_Held & 2) == 0)
						p->Ctrl_Press |= 2;
					p->Ctrl_Held |= 2;
				}
				else
					p->Ctrl_Held &= 0xFD;

				if(p->CtrlInput & 4){
					if((p->Ctrl_Held & 4) == 0)
						p->Ctrl_Press |= 4;
					p->Ctrl_Held |= 4;
				}
				else
					p->Ctrl_Held &= 0xFB;

				if(p->CtrlInput & 8){
					if((p->Ctrl_Held & 8) == 0)
						p->Ctrl_Press |= 8;
					p->Ctrl_Held |= 8;
				}
				else
					p->Ctrl_Held &= 0xF7;

				if(p->CtrlInput & 0x40){
					if((p->Ctrl_Held & 0x40) == 0)
						p->Ctrl_Press |= 0x40;
					p->Ctrl_Held |= 0x40;
				}
				else
					p->Ctrl_Held &= 0xBF;

				if(p->CtrlInput & 0x10){
					if((p->Ctrl_Held & 0x10) == 0)
						p->Ctrl_Press |= 0x10;
					p->Ctrl_Held |= 0x10;
				}
				else
					p->Ctrl_Held &= 0xEF;

				if(p->CtrlInput & 0x20){
					if((p->Ctrl_Held & 0x20) == 0)
						p->Ctrl_Press |= 0x20;
					p->Ctrl_Held |= 0x20;
				}
				else
					p->Ctrl_Held &= 0xDF;

				if(p->CtrlInput & 0x80){
					if((p->Ctrl_Held & 0x80) == 0)
						p->Ctrl_Press |= 0x80;
					p->Ctrl_Held |= 0x80;
				}
				else
					p->Ctrl_Held &= 0x7F;



				/*if(key[KEY_0])		LoadSavestate("sonic2.gs0");
				else if(key[KEY_1])	LoadSavestate("sonic2.gs1");
				else if(key[KEY_2])	LoadSavestate("sonic2.gs2");
				else if(key[KEY_3])	LoadSavestate("sonic2.gs3");
				else if(key[KEY_4])	LoadSavestate("sonic2.gs4");
				else if(key[KEY_5])	LoadSavestate("sonic2.gs5");
				else if(key[KEY_6])	LoadSavestate("sonic2.gs6");
				else if(key[KEY_7])	LoadSavestate("sonic2.gs7");
				else if(key[KEY_8])	LoadSavestate("sonic2.gs8");
				else if(key[KEY_9])	LoadSavestate("sonic2.gs9");*/
			}


			if(!typing){
				if(key[KEY_M]){
					if(key_m_previously_pressed == 0){
						show_menu ^= 1;
						key_m_previously_pressed = 1;
					}
				}
				else
					key_m_previously_pressed = 0;


				if(key[KEY_N]){
					if(key_n_previously_pressed == 0){
						debug_show_object_nodes ^= 1;
						key_n_previously_pressed = 1;
					}
				}
				else
					key_n_previously_pressed = 0;
			}


			if(frame == 1){
				if(prompt){
					LoadZonePrompt();
					prompt = 0;
				}

				if(!typing){
					if(key[KEY_SPACE]){
						edit_mode++;
						edit_mode %= 5;
						#ifdef ENABLE_MOUSE
							mouse_z_previous = mouse_z;
						#endif
						rest(200);
					}

					if(key[KEY_0]){
						rest(200);
						show_fps ^= 1;
					}
					if(key[KEY_9]){
						rest(200);
						vsync_enable ^= 1;
					}
				}
			}



			if(frame == 1){
				while(ticUpdated == 0);		// wait until 'ticUpdated' it 1
				ticUpdated = 0;
				draw_frame = 0;
			}

			if(ticCounterR % (frame_skip+1) == 0){
				draw_frame = 1;
				clear_to_color(LayerH[frame-1], MASK_COLOR_16);
				clear_to_color(LayerSH[frame-1], MASK_COLOR_16);
				if(num_of_frames > 1 && frame == 1) clear(LayerM);
			}

			if(Pause() == 0){
				if(camera_mode){
					if(p->Ctrl_Held & 1){
						p->Camera_Y_pos -= 8;
						if(key[KEY_LSHIFT] || key[KEY_RSHIFT])
							p->Camera_Y_pos -= 8;
					}
					else if(p->Ctrl_Held & 2){
						p->Camera_Y_pos += 8;
						if(key[KEY_LSHIFT] || key[KEY_RSHIFT])
							p->Camera_Y_pos += 8;
					}

					if(p->Ctrl_Held & 4){
						p->Camera_X_pos -= 8;
						if(key[KEY_LSHIFT] || key[KEY_RSHIFT])
							p->Camera_X_pos -= 8;
					}
					else if(p->Ctrl_Held & 8){
						p->Camera_X_pos += 8;
						if(key[KEY_LSHIFT] || key[KEY_RSHIFT])
							p->Camera_X_pos += 8;
					}

					if(p->Camera_X_pos < 0)
						p->Camera_X_pos = 0;
					if(p->Camera_Y_pos < 0)
						p->Camera_Y_pos = 0;

					#ifdef ENABLE_MOUSE
						player[0].x_pos = player[0].Camera_X_pos + ((mouse_x << screen_scale_x) >> screen_double_x);
						player[0].y_pos = player[0].Camera_Y_pos + ((mouse_y << screen_scale_y) >> screen_double_y);
					#endif
				}
				else{
					if(Update_HUD_timer == 1){
						if(Timer_minute == 9 && Timer_second == 59 && Timer_millisecond == 59){
							if(!Time_Over_flag){
								KillCharacter();
								Time_Over_flag = 1;
							}
						}
						else if(frame == 1){
							Timer_millisecond++;
							if(Timer_millisecond == 60){
								Timer_millisecond = 0;
								Timer_second++;
								if(Timer_second == 60){
									Timer_second = 0;
									Timer_minute++;
								}
							}
						}
					}
					else{
						if(p->anim == 5)
							p->anim_frame = 0;
					}
				}

				#ifdef ENABLE_SRAM_LOG_ANALYSIS
				if(frame == 1 && DemoPosition > 6 && /*ticCounterL >= sram_start && */titlecard_sequence_end)
					AnalyzeSRAM();
				#endif

				#ifdef ENABLE_LOGGING
					ResetLog();
				#endif

				RunFunction(1);

				if(!camera_mode)
					Obj01();

				if(!titlecard_sequence_end){
					p->x_vel = 0;
					p->y_vel = 0;
				}

				#ifdef ENABLE_LOGGING
					#ifndef ENABLE_SRAM_LOG_ANALYSIS
						DumpLog("obj01.log");
					#endif
				#endif
			}

			#ifndef ALLEGRO_DOS
			if(frame == 1){
				if(net_type > 0){
					if(net_type == 1)
						ManageNetwork();
					else{
						n = 0xFF;
						i = 0;
						while(i != 1){
							i = nlWrite(prosonicnet, &n, 1);		// player ready
							if(key[KEY_ESC])
								quit(0);
						}
						i = 0;
						while(i != 1){
							i = nlWrite(prosonicnet, &net_id[0], 1);	// who sent it
							if(key[KEY_ESC])
								quit(0);
						}
						//allegro_message("client sent data:\ni = %i\nnet_id[0] = %i", i, net_id[0]);
					}
					GetNetworkData();
				}
			}
			#endif

			if(p->Ctrl_Press & 0x10){
				camera_mode ^= 1;

				if(camera_mode){
					p->status = 0;
					p->routine = 0;
					p->layer = 0x10;
					p->x_pos_pre = 0;
					p->y_pos_pre = 0;
					p->x_vel = 0;
					p->y_vel = 0;
					p->inertia = 0;
					p->anim = 0;
					p->anim_frame = 0;
					p->anim_frame_duration = 0;
					p->next_anim = 0;
					p->Camera_Y_pos_bias = 0x60;
				}
			}

			if(camera_mode && frame == 1){
				p->x_pos = 0xFFFF;
				p->y_pos = 0xFFFF;
			}

			ManageObjectNodes(&z);

			RunFunction(2);

			if(draw_frame){
				DrawBackArea(LayerB[frame-1]);
				DrawPlayArea(LayerL[frame-1], LayerH[frame-1]);
			}

			RunFunction(3);

			if(show_fps)	DrawCounter10(LayerSH[frame-1], fps, 8, 0, 2, 1);

			if(titlecard_sequence_end){
				#ifndef ALLEGRO_DOS
				if(frame == 1){
					Talk();
				}
				#endif

				if(show_version){
					#ifdef ALLEGRO_LINUX
					DrawText(screen_resolution_x - (26 << 3), screen_resolution_y - 32, 0x10, "PROGRAMMED BY DAMIAN GROVE");
					DrawText(screen_resolution_x - (27 << 3), screen_resolution_y - 20, 0x10, "LINUX PORT BY KING INUYASHA");
					#else
						#ifdef ALLEGRO_MACOSX
						DrawText(screen_resolution_x - (26 << 3), screen_resolution_y - 32, 0x10, "PROGRAMMED BY DAMIAN GROVE");
						DrawText(screen_resolution_x - (24 << 3), screen_resolution_y - 20, 0x10, "MAC PORT BY CYBERKITSUNE");
						#else
							DrawText(screen_resolution_x - (26 << 3), screen_resolution_y - 20, 0x10, "PROGRAMMED BY DAMIAN GROVE");
						#endif
					#endif

					DrawText(screen_resolution_x - (11 << 3), screen_resolution_y - 8, 0x10, __DATE__);
				}

				if(edit_mode == 0){
					// SCORE
					DrawSprite(&z, LayerSH[frame-1], 0x105, 0x01, NORMAL, 0, 0x10+16, 0x08);
					DrawSprite(&z, LayerSH[frame-1], 0x105, 0x08, NORMAL, 0, 0x68+16, 0x08);
					//DrawCounter10(LayerSH[frame-1], p->Score, 0x60, 0x08, 0, 0);

					// TIME
					if(Timer_minute == 9){
						if(((Timer_second * 60) + Timer_millisecond) & 8)
							DrawSprite(&z, LayerSH[frame-1], 0x105, 0x03, NORMAL, 0, 0x10+16, 0x18);
						else
							DrawSprite(&z, LayerSH[frame-1], 0x105, 0x04, NORMAL, 0, 0x10+16, 0x18);
					}
					else
						DrawSprite(&z, LayerSH[frame-1], 0x105, 0x03, NORMAL, 0, 0x10+16, 0x18);
					DrawSprite(&z, LayerSH[frame-1], 0x105, 0x07, NORMAL, 0, 0x40+16, 0x18);
					//DrawCounter10(LayerSH[frame-1], Timer_minute, 0x38, 0x18, 1, 0);
					//DrawCounter10(LayerSH[frame-1], Timer_second, 0x50, 0x18, 2, 0);

					// RINGS
					if(p->Ring_count <= 0){
						if(((Timer_second * 60) + Timer_millisecond) & 8)
							DrawSprite(&z, LayerSH[frame-1], 0x105, 0x05, NORMAL, 0, 0x10+16, 0x28);
						else
							DrawSprite(&z, LayerSH[frame-1], 0x105, 0x06, NORMAL, 0, 0x10+16, 0x28);
					}
					else
						DrawSprite(&z, LayerSH[frame-1], 0x105, 0x05, NORMAL, 0, 0x10+16, 0x28);
					//DrawCounter10(LayerSH[frame-1], p->Ring_count, 0x50, 0x28, 1, 0);

					// LIVES
					DrawSprite(&z, LayerSH[frame-1], 0x105, 0x1C, NORMAL, 0, 0x10+16, screen_resolution_y - 24);
					//DrawCounter10(LayerSH[frame-1], p->Life_count, 0x38, screen_resolution_y - 16, 1, 1);
				}

				if(debug_show_object_nodes > 0){
					number_of_objects_in_memory = 0;
					for(i=0; i < (MAX_BUFFER_OBJECTS >> 3); i++){
						if(ObjectNodes[0+(i<<3)] < MAX_LEVEL_OBJECTS)
							number_of_objects_in_memory++;
						if(ObjectNodes[1+(i<<3)] < MAX_LEVEL_OBJECTS)
							number_of_objects_in_memory++;
						if(ObjectNodes[2+(i<<3)] < MAX_LEVEL_OBJECTS)
							number_of_objects_in_memory++;
						if(ObjectNodes[3+(i<<3)] < MAX_LEVEL_OBJECTS)
							number_of_objects_in_memory++;
						if(ObjectNodes[4+(i<<3)] < MAX_LEVEL_OBJECTS)
							number_of_objects_in_memory++;
						if(ObjectNodes[5+(i<<3)] < MAX_LEVEL_OBJECTS)
							number_of_objects_in_memory++;
						if(ObjectNodes[6+(i<<3)] < MAX_LEVEL_OBJECTS)
							number_of_objects_in_memory++;
						if(ObjectNodes[7+(i<<3)] < MAX_LEVEL_OBJECTS)
							number_of_objects_in_memory++;
					}
					DrawCounter16(LayerH[frame-1], number_of_objects_in_memory, 0, screen_resolution_y-MAX_BUFFER_OBJECTS-16);

					for(i=0; i < (MAX_BUFFER_OBJECTS >> 3); i++){
						DrawCounter16(LayerH[frame-1], ObjectNodes[0+(i<<3)], 0, screen_resolution_y-MAX_BUFFER_OBJECTS+(i<<3));
						DrawCounter16(LayerH[frame-1], ObjectNodes[1+(i<<3)], 40, screen_resolution_y-MAX_BUFFER_OBJECTS+(i<<3));
						DrawCounter16(LayerH[frame-1], ObjectNodes[2+(i<<3)], 80, screen_resolution_y-MAX_BUFFER_OBJECTS+(i<<3));
						DrawCounter16(LayerH[frame-1], ObjectNodes[3+(i<<3)], 120, screen_resolution_y-MAX_BUFFER_OBJECTS+(i<<3));
						DrawCounter16(LayerH[frame-1], ObjectNodes[4+(i<<3)], 160, screen_resolution_y-MAX_BUFFER_OBJECTS+(i<<3));
						DrawCounter16(LayerH[frame-1], ObjectNodes[5+(i<<3)], 200, screen_resolution_y-MAX_BUFFER_OBJECTS+(i<<3));
						DrawCounter16(LayerH[frame-1], ObjectNodes[6+(i<<3)], 240, screen_resolution_y-MAX_BUFFER_OBJECTS+(i<<3));
						DrawCounter16(LayerH[frame-1], ObjectNodes[7+(i<<3)], 280, screen_resolution_y-MAX_BUFFER_OBJECTS+(i<<3));
					}
				}

				// Draw URL in the lower-right portion of the screen
				if(show_watermark)
					DrawSprite(&z, LayerL[frame-1], 0x101, 0, NORMAL, 24, screen_resolution_x-147, screen_resolution_y-24);

				if(show_menu > 0)
					Menu();

				#ifdef ENABLE_MOUSE
					ShowBlockInfo(&z);
				#endif
			}



			#ifndef ENABLE_FRAME_CONTROL
			if(digi_card != DIGI_NONE){
				if(frame == num_of_frames)
					ProcessSound();
			}
			#endif



			if(num_of_frames > 1){
				switch(frame){
					case 1:
						if(num_of_frames == 2){
							blit(LayerB[frame-1], LayerM, 16, 0, screen_offset_x+16, screen_offset_y >> 1 << screen_double_y, screen_buffer_x-32, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerL[frame-1], LayerM, 16, 0, screen_offset_x+16, screen_offset_y >> 1 << screen_double_y, screen_buffer_x-32, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerH[frame-1], LayerM, 16, 0, screen_offset_x+16, screen_offset_y >> 1 << screen_double_y, screen_buffer_x-32, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerSH[frame-1], LayerM, 16, 0, screen_offset_x+16, screen_offset_y >> 1 << screen_double_y, screen_buffer_x-32, screen_buffer_y >> 1 << screen_double_y);
						}
						else{
							blit(LayerB[frame-1], LayerM, 8 << screen_double_x, 0, screen_offset_x+16, screen_offset_y >> 1 << screen_double_y, (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerL[frame-1], LayerM, 8 << screen_double_x, 0, screen_offset_x+16, screen_offset_y >> 1 << screen_double_y, (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerH[frame-1], LayerM, 8 << screen_double_x, 0, screen_offset_x+16, screen_offset_y >> 1 << screen_double_y, (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerSH[frame-1], LayerM, 8 << screen_double_x, 0, screen_offset_x+16, screen_offset_y >> 1 << screen_double_y, (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
						}
						break;
					case 2:
						if(num_of_frames == 2){
							blit(LayerB[frame-1], LayerM, 16, 0, screen_offset_x+16, (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), screen_buffer_x-32, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerL[frame-1], LayerM, 16, 0, screen_offset_x+16, (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), screen_buffer_x-32, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerH[frame-1], LayerM, 16, 0, screen_offset_x+16, (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), screen_buffer_x-32, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerSH[frame-1], LayerM, 16, 0, screen_offset_x+16, (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), screen_buffer_x-32, screen_buffer_y >> 1 << screen_double_y);
						}
						else{
							blit(LayerB[frame-1], LayerM, 8 << screen_double_x, 0, -((8 << screen_double_x)-16) + (((screen_resolution_x + screen_padding_x) >> 1) << screen_double_x) + (screen_padding_x >> 2 << screen_double_x) + (8 << screen_double_x), screen_offset_y >> 1 << screen_double_y, (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerL[frame-1], LayerM, 8 << screen_double_x, 0, -((8 << screen_double_x)-16) + (((screen_resolution_x + screen_padding_x) >> 1) << screen_double_x) + (screen_padding_x >> 2 << screen_double_x) + (8 << screen_double_x), screen_offset_y >> 1 << screen_double_y, (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerH[frame-1], LayerM, 8 << screen_double_x, 0, -((8 << screen_double_x)-16) + (((screen_resolution_x + screen_padding_x) >> 1) << screen_double_x) + (screen_padding_x >> 2 << screen_double_x) + (8 << screen_double_x), screen_offset_y >> 1 << screen_double_y, (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerSH[frame-1], LayerM, 8 << screen_double_x, 0, -((8 << screen_double_x)-16) + (((screen_resolution_x + screen_padding_x) >> 1) << screen_double_x) + (screen_padding_x >> 2 << screen_double_x) + (8 << screen_double_x), screen_offset_y >> 1 << screen_double_y, (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
						}
						break;
					case 3:
						blit(LayerB[frame-1], LayerM, 8 << screen_double_x, 0, screen_offset_x+16, (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
						masked_blit(LayerL[frame-1], LayerM, 8 << screen_double_x, 0, screen_offset_x+16, (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
						masked_blit(LayerH[frame-1], LayerM, 8 << screen_double_x, 0, screen_offset_x+16, (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
						masked_blit(LayerSH[frame-1], LayerM, 8 << screen_double_x, 0, screen_offset_x+16, (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
						break;
					case 4:
						blit(LayerB[frame-1], LayerM, 8 << screen_double_x, 0, -((8 << screen_double_x)-16) + (((screen_resolution_x + screen_padding_x) >> 1) << screen_double_x) + (screen_padding_x >> 2 << screen_double_x) + (8 << screen_double_x), (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
						masked_blit(LayerL[frame-1], LayerM, 8 << screen_double_x, 0, -((8 << screen_double_x)-16) + (((screen_resolution_x + screen_padding_x) >> 1) << screen_double_x) + (screen_padding_x >> 2 << screen_double_x) + (8 << screen_double_x), (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
						masked_blit(LayerH[frame-1], LayerM, 8 << screen_double_x, 0, -((8 << screen_double_x)-16) + (((screen_resolution_x + screen_padding_x) >> 1) << screen_double_x) + (screen_padding_x >> 2 << screen_double_x) + (8 << screen_double_x), (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
						masked_blit(LayerSH[frame-1], LayerM, 8 << screen_double_x, 0, -((8 << screen_double_x)-16) + (((screen_resolution_x + screen_padding_x) >> 1) << screen_double_x) + (screen_padding_x >> 2 << screen_double_x) + (8 << screen_double_x), (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
				}

				if(frame == num_of_frames){
					if(fade_count != 0) FadeRGB(LayerM);
					TitleCard();
					if(vsync_enable)
						vsync();
					#ifndef ALLEGRO_DOS
					acquire_screen();	// used for "set_display_switch_mode()"
					#endif
					if(screen_double_x && num_of_frames == 2)
						stretch_blit(LayerM, screen, 16, 0, screen_resolution_x+screen_padding_x, (screen_resolution_y+screen_padding_y) << screen_double_y, 0, 0, (screen_resolution_x+screen_padding_x) << screen_double_x, (screen_resolution_y+screen_padding_y) << screen_double_y);
					else
						blit(LayerM, screen, 16, 0, 0, 0, (screen_resolution_x+screen_padding_x) << screen_double_x, (screen_resolution_y+screen_padding_y) << screen_double_y);
					#ifndef ALLEGRO_DOS
					release_screen();	// used for "set_display_switch_mode()"
					#endif
				}
			}
			else{
				masked_blit(LayerL[frame-1], LayerB[frame-1], 16, 0, 16, 0, screen_buffer_x-32, screen_buffer_y);
				masked_blit(LayerH[frame-1], LayerB[frame-1], 16, 0, 16, 0, screen_buffer_x-32, screen_buffer_y);
				masked_blit(LayerSH[frame-1], LayerB[frame-1], 16, 0, 16, 0, screen_buffer_x-32, screen_buffer_y);
				if(fade_count != 0) FadeRGB(LayerB[frame-1]);
				TitleCard();
				if(vsync_enable)
					vsync();
				#ifndef ALLEGRO_DOS
				acquire_screen();	// used for "set_display_switch_mode()"
				#endif
				if(screen_double_x || screen_double_y)
					stretch_blit(LayerB[frame-1], screen, 16, 0, screen_resolution_x, screen_resolution_y, screen_offset_x << screen_double_x, screen_offset_y << screen_double_y, screen_resolution_x << screen_double_x, screen_resolution_y << screen_double_y);
				else
					blit(LayerB[frame-1], screen, 16, 0, screen_offset_x, screen_offset_y, screen_resolution_x, screen_resolution_y);
				#ifndef ALLEGRO_DOS
				release_screen();	// used for "set_display_switch_mode()"
				#endif
			}






			if(frame == 1){		// shouldn't this be "frame == num_of_frames"??
				// Water movement
				if(water_direction == 1 && water_accel == 63){
					water_accel = 63;
					water_direction = 0;
				}
				else if(water_direction == 0 && water_accel == -63){
					water_accel = -63;
					water_direction = 1;
				}
				if(water_direction == 0)
					water_accel--;
				else
					water_accel++;
				water_current_height += water_accel;



				// Counter FE74
				if(counter_fe74_direction == 1 && counter_fe74_accel == 0xB0){
					counter_fe74_accel = 0xB0;
					counter_fe74_direction = 0;
				}
				else if(counter_fe74_direction == 0 && counter_fe74_accel == -0xB0){
					counter_fe74_accel = -0xB0;
					counter_fe74_direction = 1;
				}
				if(counter_fe74_direction == 0)
					counter_fe74_accel -= 8;
				else
					counter_fe74_accel += 8;
				counter_fe74 += counter_fe74_accel;
			}







			if(Level_Inactive_flag && frame == num_of_frames){
				if(fade_count == -96){
					if(zone)
						unlisted_zone = 0;
					ResetSound();
					Level_Inactive_flag = 0;
					LoadZone(&z, zone_file_list[zone + (unlisted_zone * 256)]);
					InitDraw(&z);
					AllocateObjects(&z);
					CreateBlockLayout(&z);

					strcpy(TextBuffer, "ProSonic - ");
					strcat(TextBuffer, z.LevelName1);
					strcat(TextBuffer, " ZONE");
					set_window_title(TextBuffer);

					// SET UP THE CELL DATA
					num_of_cells = z.FILTERCOMPOSITE[0];
					num_of_filters = z.FILTERCOMPOSITE[1];
					cells_ptr = *(short *)&z.FILTERCOMPOSITE[2];
					filters_ptr = *(short *)&z.FILTERCOMPOSITE[4];
					bg_rgb_color = *(short *)&z.FILTERCOMPOSITE[6];
					filter_a = z.FILTERCOMPOSITE[8];
					filter_b = z.FILTERCOMPOSITE[9];
					filter_a_speed = z.FILTERCOMPOSITE[10];
					filter_b_speed = z.FILTERCOMPOSITE[11];

					cell_data = (CELL*)(&z.FILTERCOMPOSITE[cells_ptr]);

					for(i=0; i < num_of_frames; i++){
						n = player[i].Life_count;
						d0 = player[i].Saved_Last_star_pole_hit;
						d1 = player[i].Saved_x_pos;
						d2 = player[i].Saved_y_pos;
						d3 = player[i].Saved_Ring_count;
						d4 = player[i].Saved_Timer;
						d5 = player[i].Saved_art_tile;
						d6 = player[i].Saved_layer;
						d7 = player[i].Saved_Camera_X_pos;
						a0 = player[i].Saved_Camera_Y_pos;
						a1 = player[i].Saved_Water_Level;
						a2 = player[i].Saved_Extra_life_flags;
						a3 = player[i].Saved_Extra_life_flags_2P;
						a4 = player[i].Saved_Camera_Max_Y_pos;

						/*
						player[i] = init_player;
						/*/
						player[i].status = 0;
						player[i].routine = 0;
						player[i].layer = 0x10;
						player[i].x_pos_pre = 0;
						player[i].y_pos_pre = 0;
						player[i].x_vel = 0;
						player[i].y_vel = 0;
						player[i].inertia = 0;
						player[i].anim = 0;
						player[i].anim_frame = 0;
						player[i].anim_frame_duration = 0;
						player[i].next_anim = 0;
						//*/

						player[i].Life_count = n;
						player[i].Saved_Last_star_pole_hit = d0;
						player[i].Saved_x_pos = d1;
						player[i].Saved_y_pos = d2;
						player[i].Saved_Ring_count = d3;
						player[i].Saved_Timer = d4;
						player[i].Saved_art_tile = d5;
						player[i].Saved_layer = d6;
						player[i].Saved_Camera_X_pos = d7;
						player[i].Saved_Camera_Y_pos = a0;
						player[i].Saved_Water_Level = a1;
						player[i].Saved_Extra_life_flags = a2;
						player[i].Saved_Extra_life_flags_2P = a3;
						player[i].Saved_Camera_Max_Y_pos = a4;

						//if(player[i].Saved_x_pos != 0){
						//	player[i].x_pos = player[i].Saved_x_pos;
						//	player[i].y_pos = player[i].Saved_y_pos;
						//}
						//else{
							player[i].x_pos = z.Act[act].Stage[stage].StartX[0];
							player[i].y_pos = z.Act[act].Stage[stage].StartY[0];
						//}

						player[i].Camera_X_pos = player[i].x_pos - (screen_resolution_x >> 1);
						player[i].Camera_Y_pos = player[i].y_pos - (screen_resolution_y >> 1) + 0x10;
						player[i].Camera_Min_X_pos = z.Act[act].Stage[stage].LevelXL;
						player[i].Camera_Max_X_pos = z.Act[act].Stage[stage].LevelXR;
						player[i].Camera_Min_Y_pos = z.Act[act].Stage[stage].LevelYT;
						player[i].Camera_Max_Y_pos = z.Act[act].Stage[stage].LevelYB;
						player[i].Camera_Max_Y_pos_now = z.Act[act].Stage[stage].LevelYB;
						player[i].Camera_Y_pos_bias = 0x60;

						player[i].render_flags |= 0x80;

						player[i].Ring_count = 0;
					}
					Timer_minute = 0;
					Timer_second = 0;
					Timer_millisecond = 0;

					water_accel = 0;
					water_direction = 1;
					counter_fe74_accel = 0;
					counter_fe74_direction = 1;
					counter_fe74 = 0x80;

					Game_paused = 0;

					titlecard_sequence_end = 0;

					RunFunction(4);
				}
				else{
					FadeOutFM(0);
					fade_count -= 4;
				}
			}







			if(!Game_paused && frame == num_of_frames){
				ticCounter60L++;
				ticCounterL++;
			}
		}
	}//END KEY_ESC

	quit(ERROR_NULL);
	return 0;	// Not really needed, this just stops the compiler from complaining ;)
}
END_OF_MAIN()
